@brainervirus/workit-core 5.0.0 → 6.0.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/README.md +16 -34
- package/package.json +1 -1
- package/src/autonomy.ts +309 -44
- package/src/core/context-read.ts +144 -0
- package/src/core/methods.ts +7 -4
- package/src/core/store-lock.ts +1 -1
- package/src/core/task-context.ts +1 -1
- package/src/core/task-contract.ts +8 -130
- package/src/core/task-engine.ts +57 -879
- package/src/core/task-evaluation.ts +63 -5
- package/src/core/task-store.ts +2 -107
- package/src/core/workers.ts +12 -158
- package/src/core/workspaces.ts +15 -0
- package/src/core/youtrack-writes.ts +92 -0
- package/src/core.ts +2 -79
- package/src/forge/pr-ops.ts +2 -2
- package/src/hooks/handle.ts +1 -1
- package/src/hooks/hosts/claude-code.ts +0 -8
- package/src/hooks/hosts/codex.ts +2 -11
- package/src/hooks/hosts/cursor.ts +0 -8
- package/src/hooks/hosts/opencode.ts +0 -8
- package/src/hooks/hosts/pi.ts +0 -10
- package/templates/workit-contract.md +3 -2
- package/src/core/authority.ts +0 -1033
- package/src/core/auto-approval.ts +0 -338
- package/src/core/external-action-effects.ts +0 -2441
- package/src/core/external-action.ts +0 -1204
package/src/core/store-lock.ts
CHANGED
|
@@ -20,7 +20,6 @@ export type MetadataLock = {
|
|
|
20
20
|
processStart: string | null;
|
|
21
21
|
host: string;
|
|
22
22
|
nonce: string;
|
|
23
|
-
externalAction?: true;
|
|
24
23
|
};
|
|
25
24
|
|
|
26
25
|
const metadataLockSchema = z
|
|
@@ -29,6 +28,7 @@ const metadataLockSchema = z
|
|
|
29
28
|
processStart: z.string().nullable(),
|
|
30
29
|
host: z.string().min(1),
|
|
31
30
|
nonce: z.string().min(1),
|
|
31
|
+
/** Written by ≤4.x managed external actions; read and ignored (D17). */
|
|
32
32
|
externalAction: z.literal(true).optional(),
|
|
33
33
|
})
|
|
34
34
|
.strict();
|
package/src/core/task-context.ts
CHANGED
|
@@ -109,7 +109,7 @@ const compactTimestamp = (value: Utc): string =>
|
|
|
109
109
|
`${value.slice(0, 19)}.${value.slice(20, -1).padEnd(9, "0")}`;
|
|
110
110
|
const compactDecisionChoice = (entry: TaskView["task"]["decisions"][number]): string | null => {
|
|
111
111
|
const stated = entry.data.binding.statedChoice?.text;
|
|
112
|
-
const approved = entry.data.response === "approved" ? entry.data.binding.
|
|
112
|
+
const approved = entry.data.response === "approved" ? entry.data.binding.presented : "";
|
|
113
113
|
const source = stated?.trim() || approved.trim() || null;
|
|
114
114
|
return compactText(source, COMPACT_TEXT_BYTES);
|
|
115
115
|
};
|
|
@@ -10,7 +10,6 @@ export const OPERATION_FAMILIES = [
|
|
|
10
10
|
"finding",
|
|
11
11
|
"decision",
|
|
12
12
|
"worker",
|
|
13
|
-
"writer",
|
|
14
13
|
"state",
|
|
15
14
|
] as const;
|
|
16
15
|
export type OperationFamily = (typeof OPERATION_FAMILIES)[number];
|
|
@@ -145,19 +144,6 @@ export const refSchema = z.discriminatedUnion("kind", [
|
|
|
145
144
|
.strict(),
|
|
146
145
|
z.object({ kind: z.literal("host"), host: hostSchema, handle: nonEmpty }).strict(),
|
|
147
146
|
z.object({ kind: z.literal("external"), url: text.url() }).strict(),
|
|
148
|
-
// Standing auto-approval receipt: authorizes without a live question while
|
|
149
|
-
// the referenced workspace rule covers the operation class. Verified live
|
|
150
|
-
// at reserve time against current config; dropped on export/import (a rule
|
|
151
|
-
// from another machine must never authorize here). Scalar-only shape keeps
|
|
152
|
-
// advertised operation schemas within provider depth limits.
|
|
153
|
-
z
|
|
154
|
-
.object({
|
|
155
|
-
kind: z.literal("standing"),
|
|
156
|
-
workspace: nonEmpty,
|
|
157
|
-
class: nonEmpty,
|
|
158
|
-
configDigest: nullableDigest,
|
|
159
|
-
})
|
|
160
|
-
.strict(),
|
|
161
147
|
]);
|
|
162
148
|
export type Ref = z.infer<typeof refSchema>;
|
|
163
149
|
|
|
@@ -274,7 +260,6 @@ export const provenanceSchema = z
|
|
|
274
260
|
host: hostSchema,
|
|
275
261
|
session: refSchema.nullable(),
|
|
276
262
|
workerId: nullableId,
|
|
277
|
-
receipts: z.array(refSchema),
|
|
278
263
|
})
|
|
279
264
|
.strict();
|
|
280
265
|
export type Provenance = z.infer<typeof provenanceSchema>;
|
|
@@ -622,6 +607,13 @@ export const evidenceEvaluationSchema = z
|
|
|
622
607
|
})
|
|
623
608
|
.strict();
|
|
624
609
|
export type EvidenceEvaluation = z.infer<typeof evidenceEvaluationSchema>;
|
|
610
|
+
/**
|
|
611
|
+
* A durable decision record (D2, D18): agent-asserted like every other
|
|
612
|
+
* recording. It satisfies `decisions` requirements and accepted limitations;
|
|
613
|
+
* it never authorizes an external effect — autonomy grants and the host's
|
|
614
|
+
* own permission prompt do that. Legacy keys (`approvedContent`, `displayed`,
|
|
615
|
+
* `standing`, `consumption`) are dropped on read (D17).
|
|
616
|
+
*/
|
|
625
617
|
export const decisionSchema = z
|
|
626
618
|
.object({
|
|
627
619
|
purpose: z.enum(["design", "action", "limitation", "preference"]),
|
|
@@ -631,53 +623,18 @@ export const decisionSchema = z
|
|
|
631
623
|
workspaceId: id,
|
|
632
624
|
scope: scopeSchema,
|
|
633
625
|
presented: text,
|
|
634
|
-
approvedContent: text,
|
|
635
|
-
displayed: text.optional(),
|
|
636
626
|
contentRefs: z.array(refSchema),
|
|
637
627
|
statedChoice: z.object({ ref: text, text: text }).strict().optional(),
|
|
638
|
-
standing: z.object({ workspace: nonEmpty, class: nonEmpty }).strict().optional(),
|
|
639
628
|
})
|
|
640
629
|
.strict(),
|
|
641
630
|
digest,
|
|
642
631
|
response: z.enum(["approved", "rejected", "stated"]),
|
|
643
632
|
requirementIds: z.array(digest),
|
|
644
633
|
revoked: z.object({ at: utc, reason: text }).strict().nullable(),
|
|
645
|
-
consumption: z
|
|
646
|
-
.object({
|
|
647
|
-
state: z.enum(["reserved", "consumed", "uncertain"]),
|
|
648
|
-
at: utc,
|
|
649
|
-
actionRef: refSchema,
|
|
650
|
-
/** Historical identifier only; never interpreted as native authority. */
|
|
651
|
-
historicalActionId: text.optional(),
|
|
652
|
-
})
|
|
653
|
-
.strict()
|
|
654
|
-
.nullable(),
|
|
655
634
|
})
|
|
656
635
|
.strict();
|
|
657
636
|
export type Decision = z.infer<typeof decisionSchema>;
|
|
658
637
|
|
|
659
|
-
/** Show-before-ask guard: binding questions are single-sentence and never
|
|
660
|
-
* carry the artifact, descriptor, or payload body in the question UI. */
|
|
661
|
-
export const BINDING_QUESTION_BUDGET = 300;
|
|
662
|
-
const bindingQuestionHeader = /^Workit decision: (design|action|limitation|preference)$/;
|
|
663
|
-
export const workitBindingQuestionIssue = (questions: unknown): string | null => {
|
|
664
|
-
if (!Array.isArray(questions) || questions.length !== 1) return null;
|
|
665
|
-
const question = questions[0] as { header?: unknown; question?: unknown; options?: unknown };
|
|
666
|
-
if (!question || typeof question !== "object") return null;
|
|
667
|
-
if (typeof question.header !== "string" || !bindingQuestionHeader.test(question.header.trim()))
|
|
668
|
-
return null;
|
|
669
|
-
const over = (label: string, value: string) =>
|
|
670
|
-
`invalid_input: ${label} is ${value.length} characters; Workit binding questions must stay within ${BINDING_QUESTION_BUDGET}. present the item in the conversation first, then ask a short scoped question.`;
|
|
671
|
-
if (typeof question.question === "string" && question.question.length > BINDING_QUESTION_BUDGET)
|
|
672
|
-
return over("the question text", question.question);
|
|
673
|
-
if (Array.isArray(question.options))
|
|
674
|
-
for (const option of question.options) {
|
|
675
|
-
const description = (option as { description?: unknown }).description;
|
|
676
|
-
if (typeof description === "string" && description.length > BINDING_QUESTION_BUDGET)
|
|
677
|
-
return over("an option description", description);
|
|
678
|
-
}
|
|
679
|
-
return null;
|
|
680
|
-
};
|
|
681
638
|
export const findingSchema = z
|
|
682
639
|
.object({
|
|
683
640
|
claim: text,
|
|
@@ -724,14 +681,6 @@ export const workerSchema = z
|
|
|
724
681
|
})
|
|
725
682
|
.strict();
|
|
726
683
|
export type Worker = z.infer<typeof workerSchema>;
|
|
727
|
-
export const ownerSchema = z
|
|
728
|
-
.object({
|
|
729
|
-
taskId: id,
|
|
730
|
-
workerId: nullableId,
|
|
731
|
-
session: z.object({ kind: z.literal("host"), host: hostSchema, handle: nonEmpty }).strict(),
|
|
732
|
-
})
|
|
733
|
-
.strict();
|
|
734
|
-
export type Owner = z.infer<typeof ownerSchema>;
|
|
735
684
|
export const closureSchema = z
|
|
736
685
|
.object({
|
|
737
686
|
outcome: outcomeSchema,
|
|
@@ -742,37 +691,6 @@ export const closureSchema = z
|
|
|
742
691
|
})
|
|
743
692
|
.strict();
|
|
744
693
|
export type Closure = z.infer<typeof closureSchema>;
|
|
745
|
-
export const actionProgressSchema = z
|
|
746
|
-
.object({
|
|
747
|
-
decisionId: id,
|
|
748
|
-
steps: z.array(nonEmpty),
|
|
749
|
-
completedSteps: z.array(nonEmpty),
|
|
750
|
-
})
|
|
751
|
-
.strict()
|
|
752
|
-
.check((ctx) => {
|
|
753
|
-
const { steps, completedSteps } = ctx.value;
|
|
754
|
-
const valid =
|
|
755
|
-
new Set(steps).size === steps.length &&
|
|
756
|
-
new Set(completedSteps).size === completedSteps.length &&
|
|
757
|
-
completedSteps.every((step, index) => steps[index] === step);
|
|
758
|
-
if (!valid)
|
|
759
|
-
ctx.issues.push({
|
|
760
|
-
code: "custom",
|
|
761
|
-
input: ctx.value,
|
|
762
|
-
message: "completed action steps must be a unique prefix of approved steps",
|
|
763
|
-
path: ["completedSteps"],
|
|
764
|
-
});
|
|
765
|
-
});
|
|
766
|
-
export type ActionProgress = z.infer<typeof actionProgressSchema>;
|
|
767
|
-
export const actionProgressListSchema = z.array(actionProgressSchema).check((ctx) => {
|
|
768
|
-
if (new Set(ctx.value.map((progress) => progress.decisionId)).size !== ctx.value.length)
|
|
769
|
-
ctx.issues.push({
|
|
770
|
-
code: "custom",
|
|
771
|
-
input: ctx.value,
|
|
772
|
-
message: "action progress decision ids must be unique",
|
|
773
|
-
path: ["decisionId"],
|
|
774
|
-
});
|
|
775
|
-
});
|
|
776
694
|
export const runtimeSchema = z
|
|
777
695
|
.object({
|
|
778
696
|
createdWith: text.nullable(),
|
|
@@ -802,7 +720,6 @@ export const taskRecordSchema = z
|
|
|
802
720
|
candidates: z.array(candidateSchema),
|
|
803
721
|
evidence: z.array(entrySchema(evidenceSchema)),
|
|
804
722
|
decisions: z.array(entrySchema(decisionSchema)),
|
|
805
|
-
actionProgress: actionProgressListSchema.optional(),
|
|
806
723
|
findings: z.array(entrySchema(findingSchema)),
|
|
807
724
|
workers: z.array(entrySchema(workerSchema)),
|
|
808
725
|
/** Paths a reader must understand; see parseStoredRecord. */
|
|
@@ -917,10 +834,6 @@ export const workspaceRecordSchema = z
|
|
|
917
834
|
revision,
|
|
918
835
|
root: nonEmpty,
|
|
919
836
|
runtime: runtimeSchema.optional(),
|
|
920
|
-
writer: z
|
|
921
|
-
.object({ state: z.enum(["held", "uncertain"]), owner: ownerSchema, acquiredAt: utc })
|
|
922
|
-
.strict()
|
|
923
|
-
.nullable(),
|
|
924
837
|
/** Paths a reader must understand; see parseStoredRecord. */
|
|
925
838
|
critical: z.array(nonEmpty).optional(),
|
|
926
839
|
})
|
|
@@ -970,7 +883,6 @@ export const taskSummarySchema = z
|
|
|
970
883
|
progress: progressSchema,
|
|
971
884
|
policy: policySchema.nullable(),
|
|
972
885
|
requirements: z.array(requirementEvaluationSchema),
|
|
973
|
-
writer: workspaceRecordSchema.shape.writer,
|
|
974
886
|
})
|
|
975
887
|
.strict();
|
|
976
888
|
export type TaskSummary = z.infer<typeof taskSummarySchema>;
|
|
@@ -1046,7 +958,6 @@ const taskOperations = {
|
|
|
1046
958
|
...taskId,
|
|
1047
959
|
...revisions,
|
|
1048
960
|
expectedWorkspaceRevision: revision.optional(),
|
|
1049
|
-
authorityRefs: z.array(refSchema).optional(),
|
|
1050
961
|
}),
|
|
1051
962
|
close: operation({
|
|
1052
963
|
action: z.literal("close"),
|
|
@@ -1141,30 +1052,12 @@ const workerOperations = {
|
|
|
1141
1052
|
reason: text,
|
|
1142
1053
|
}),
|
|
1143
1054
|
};
|
|
1144
|
-
const writerOperations = {
|
|
1145
|
-
acquire: operation({
|
|
1146
|
-
action: z.literal("acquire"),
|
|
1147
|
-
...taskId,
|
|
1148
|
-
expectedRevision: revision.optional(),
|
|
1149
|
-
expectedWorkspaceRevision: revision.optional(),
|
|
1150
|
-
workerId: nullableId.optional(),
|
|
1151
|
-
reason: text.optional(),
|
|
1152
|
-
}),
|
|
1153
|
-
release: operation({
|
|
1154
|
-
action: z.literal("release"),
|
|
1155
|
-
...taskId,
|
|
1156
|
-
expectedRevision: revision.optional(),
|
|
1157
|
-
expectedWorkspaceRevision: revision.optional(),
|
|
1158
|
-
reason: text.optional(),
|
|
1159
|
-
}),
|
|
1160
|
-
};
|
|
1161
1055
|
const stateOperations = {
|
|
1162
1056
|
export: operation({ action: z.literal("export"), ...taskId }),
|
|
1163
1057
|
import: operation({
|
|
1164
1058
|
action: z.literal("import"),
|
|
1165
1059
|
expectedWorkspaceRevision: revision.nullable().optional(),
|
|
1166
1060
|
bundle: exportBundleSchema,
|
|
1167
|
-
authorityRefs: z.array(refSchema),
|
|
1168
1061
|
}),
|
|
1169
1062
|
};
|
|
1170
1063
|
|
|
@@ -1175,7 +1068,6 @@ export const operationSchemas = {
|
|
|
1175
1068
|
finding: z.discriminatedUnion("action", Object.values(findingOperations) as any),
|
|
1176
1069
|
decision: z.discriminatedUnion("action", Object.values(decisionOperations) as any),
|
|
1177
1070
|
worker: z.discriminatedUnion("action", Object.values(workerOperations) as any),
|
|
1178
|
-
writer: z.discriminatedUnion("action", Object.values(writerOperations) as any),
|
|
1179
1071
|
state: z.discriminatedUnion("action", Object.values(stateOperations) as any),
|
|
1180
1072
|
} as const;
|
|
1181
1073
|
export type OperationRequest = z.infer<(typeof operationSchemas)[OperationFamily]>;
|
|
@@ -1198,7 +1090,6 @@ export const advertisedOperationSchemas = {
|
|
|
1198
1090
|
finding: advertised(findingOperations),
|
|
1199
1091
|
decision: advertised(decisionOperations),
|
|
1200
1092
|
worker: advertised(workerOperations),
|
|
1201
|
-
writer: advertised(writerOperations),
|
|
1202
1093
|
state: advertised(stateOperations),
|
|
1203
1094
|
} as const;
|
|
1204
1095
|
export type TaskStartRequest = z.infer<typeof taskOperations.start>;
|
|
@@ -1222,7 +1113,6 @@ export type ErrorCode =
|
|
|
1222
1113
|
| "permission_denied"
|
|
1223
1114
|
| "capability_unavailable"
|
|
1224
1115
|
| "requirements_unsatisfied"
|
|
1225
|
-
| "writer_conflict"
|
|
1226
1116
|
/** Retryable: another live Workit call holds the checkout's metadata lock. */
|
|
1227
1117
|
| "busy"
|
|
1228
1118
|
| "recovery_required"
|
|
@@ -1237,16 +1127,9 @@ export type ErrorDetails = {
|
|
|
1237
1127
|
actualWorkspaceRevision?: Revision | null;
|
|
1238
1128
|
requirementIds?: Digest[];
|
|
1239
1129
|
capability?: string;
|
|
1240
|
-
owner?: Owner;
|
|
1241
1130
|
path?: string;
|
|
1242
1131
|
operation?: string;
|
|
1243
1132
|
outcome?: "not_started" | "pending" | "unknown";
|
|
1244
|
-
/** Present when a native approval must be asked from a concise proposal. */
|
|
1245
|
-
proposal?: {
|
|
1246
|
-
presented: string;
|
|
1247
|
-
approvedContent: string;
|
|
1248
|
-
descriptorDigest: string;
|
|
1249
|
-
};
|
|
1250
1133
|
guidance?: string;
|
|
1251
1134
|
/** Structured remedy for unsatisfied requirements: rule, reason, and
|
|
1252
1135
|
* satisfaction text instead of opaque requirement hashes alone. */
|
|
@@ -1505,12 +1388,7 @@ const compareNullableText = (left: string | null, right: string | null): number
|
|
|
1505
1388
|
const compareBooleanNullable = (left: boolean | null, right: boolean | null): number =>
|
|
1506
1389
|
left === right ? 0 : left === null ? -1 : right === null ? 1 : Number(left) - Number(right);
|
|
1507
1390
|
export function decisionDigest(input: Omit<Decision, "digest"> | Decision): Digest {
|
|
1508
|
-
const {
|
|
1509
|
-
digest: _ignored,
|
|
1510
|
-
revoked: _revoked,
|
|
1511
|
-
consumption: _consumption,
|
|
1512
|
-
...value
|
|
1513
|
-
} = input as Decision;
|
|
1391
|
+
const { digest: _ignored, revoked: _revoked, ...value } = input as Decision;
|
|
1514
1392
|
return sha256(value);
|
|
1515
1393
|
}
|
|
1516
1394
|
export function candidateDigest(input: Candidate): Digest {
|