@astrosheep/keiyaku 2.9.5 → 2.9.6
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/build/.tsbuildinfo +1 -1
- package/build/agents/harness/outcome.js +10 -4
- package/build/agents/harness/projection.js +8 -7
- package/build/agents/harness/router.js +2 -1
- package/build/agents/providers/claude-agent-sdk/adapter.js +13 -1
- package/build/agents/providers/claude-agent-sdk/registration.js +2 -1
- package/build/agents/providers/codex-app-server/adapter.js +11 -10
- package/build/agents/providers/codex-app-server/registration.js +2 -1
- package/build/agents/providers/opencode-sdk/adapter.js +7 -0
- package/build/agents/providers/opencode-sdk/registration.js +2 -1
- package/build/agents/providers/pi/adapter.js +10 -0
- package/build/agents/providers/pi/registration.js +3 -1
- package/build/cli/completion.js +9 -2
- package/build/cli/flags.js +3 -2
- package/build/cli/render/errors.js +18 -1
- package/build/cli/render/format.js +5 -2
- package/build/core/audit/candidate.js +48 -0
- package/build/core/audit/coordinates.js +180 -0
- package/build/core/audit/evidence.js +158 -0
- package/build/core/audit/facade.js +42 -0
- package/build/core/audit/readiness.js +4 -0
- package/build/core/audit/report.js +38 -0
- package/build/core/audit.js +1 -650
- package/build/core/task/query.js +65 -23
- package/build/generated/version.js +2 -2
- package/package.json +1 -1
- package/skills/keiyaku-workflow/SKILL.md +6 -0
package/build/core/task/query.js
CHANGED
|
@@ -2,10 +2,16 @@ import { FlowError } from "../../flow-error.js";
|
|
|
2
2
|
import { isCommissionId } from "../ids.js";
|
|
3
3
|
import { taskUpdatedAt } from "./model.js";
|
|
4
4
|
import { isTaskId } from "./validation.js";
|
|
5
|
-
const
|
|
6
|
-
"state", "priority", "parent", "needs", "blocks", "contract", "created", "updated",
|
|
7
|
-
]
|
|
5
|
+
export const TASK_FILTER_FIELDS = [
|
|
6
|
+
"title", "state", "priority", "parent", "needs", "blocks", "contract", "created", "updated",
|
|
7
|
+
];
|
|
8
|
+
export const TASK_FILTER_OPERATORS = ["=", "!=", "~", "<", "<=", ">", ">="];
|
|
9
|
+
export const TASK_FILTER_GRAMMAR = "FIELD OP VALUE[,VALUE...] [| FIELD OP VALUE[,VALUE...]]...";
|
|
10
|
+
export const TASK_FILTER_COMPLETION = "FIELD OP VALUE[,VALUE...]|FIELD OP VALUE[,VALUE...]";
|
|
11
|
+
const FILTER_FIELDS = new Set(TASK_FILTER_FIELDS);
|
|
8
12
|
const ORDERED_FIELDS = new Set(["priority", "created", "updated"]);
|
|
13
|
+
const TITLE_OPERATORS = new Set(["=", "!=", "~"]);
|
|
14
|
+
const ORDERED_OPERATORS = new Set(["=", "!=", "<", "<=", ">", ">="]);
|
|
9
15
|
const DISPLAY_VALUES = new Set(["ready", "blocked", "in_progress", "done", "dropped", "bound"]);
|
|
10
16
|
const SORT_KEYS = new Set(["priority", "modified", "created"]);
|
|
11
17
|
const RELATIVE_TIME = /^now-([1-9][0-9]*)([smhdw])$/;
|
|
@@ -21,57 +27,87 @@ export function deriveTaskDisplay(task) {
|
|
|
21
27
|
return "in_progress";
|
|
22
28
|
return task.blocked ? "blocked" : "ready";
|
|
23
29
|
}
|
|
24
|
-
function invalidFilter(detail) {
|
|
25
|
-
throw new FlowError("INVALID_TARGET", `invalid task filter: ${detail}
|
|
30
|
+
function invalidFilter(detail, recoveredTerms = []) {
|
|
31
|
+
throw new FlowError("INVALID_TARGET", `invalid task filter: ${detail}`, {
|
|
32
|
+
facts: {
|
|
33
|
+
kind: "invalid_task_filter",
|
|
34
|
+
detail,
|
|
35
|
+
grammar: TASK_FILTER_GRAMMAR,
|
|
36
|
+
fields: [...TASK_FILTER_FIELDS],
|
|
37
|
+
operators: [...TASK_FILTER_OPERATORS],
|
|
38
|
+
recoveredTerms: recoveredTerms.map((term) => ({
|
|
39
|
+
field: term.field,
|
|
40
|
+
operator: term.operator,
|
|
41
|
+
values: [...term.values],
|
|
42
|
+
})),
|
|
43
|
+
},
|
|
44
|
+
});
|
|
26
45
|
}
|
|
27
|
-
|
|
28
|
-
const match = /^([a-z][a-z0-9_-]*)(
|
|
46
|
+
function parseTaskFilterTerm(term, expression, recoveredTerms) {
|
|
47
|
+
const match = /^([a-z][a-z0-9_-]*)(!=|<=|>=|~|=|<|>)(.*)$/.exec(term);
|
|
29
48
|
if (!match)
|
|
30
|
-
invalidFilter(`'${expression}' must use
|
|
49
|
+
invalidFilter(`'${expression}' must use ${TASK_FILTER_GRAMMAR}`, recoveredTerms);
|
|
31
50
|
const field = match[1];
|
|
32
51
|
const operator = match[2];
|
|
33
52
|
if (!FILTER_FIELDS.has(field))
|
|
34
|
-
invalidFilter(`unknown field '${field}'
|
|
35
|
-
if (
|
|
36
|
-
invalidFilter(`field '${field}' does not support operator '${operator}'
|
|
53
|
+
invalidFilter(`unknown field '${field}'`, recoveredTerms);
|
|
54
|
+
if (field === "title" && !TITLE_OPERATORS.has(operator)) {
|
|
55
|
+
invalidFilter(`field '${field}' does not support operator '${operator}'`, recoveredTerms);
|
|
56
|
+
}
|
|
57
|
+
if (field !== "title" && (ORDERED_FIELDS.has(field)
|
|
58
|
+
? !ORDERED_OPERATORS.has(operator)
|
|
59
|
+
: operator !== "=" && operator !== "!=")) {
|
|
60
|
+
invalidFilter(`field '${field}' does not support operator '${operator}'`, recoveredTerms);
|
|
37
61
|
}
|
|
38
62
|
const values = match[3].split(",");
|
|
39
63
|
if (values.some((value) => value.length === 0))
|
|
40
|
-
invalidFilter(`field '${field}' has an empty value
|
|
41
|
-
validateFilterValues(field, values);
|
|
64
|
+
invalidFilter(`field '${field}' has an empty value`, recoveredTerms);
|
|
65
|
+
validateFilterValues(field, values, recoveredTerms);
|
|
42
66
|
return { field, operator, values };
|
|
43
67
|
}
|
|
44
|
-
function
|
|
68
|
+
export function parseTaskFilter(expression) {
|
|
69
|
+
const terms = expression.split("|");
|
|
70
|
+
const parsed = [];
|
|
71
|
+
for (const term of terms) {
|
|
72
|
+
if (term.length === 0)
|
|
73
|
+
invalidFilter(`'${expression}' must use ${TASK_FILTER_GRAMMAR}`, parsed);
|
|
74
|
+
parsed.push(parseTaskFilterTerm(term, expression, parsed));
|
|
75
|
+
}
|
|
76
|
+
return parsed;
|
|
77
|
+
}
|
|
78
|
+
function parseTimestamp(value, nowMs, recoveredTerms = []) {
|
|
45
79
|
const relative = RELATIVE_TIME.exec(value);
|
|
46
80
|
if (relative) {
|
|
47
81
|
const amount = Number(relative[1]);
|
|
48
82
|
const offset = amount * UNIT_MS[relative[2]];
|
|
49
83
|
if (!Number.isSafeInteger(offset))
|
|
50
|
-
invalidFilter(`timestamp '${value}' is outside the supported range
|
|
84
|
+
invalidFilter(`timestamp '${value}' is outside the supported range`, recoveredTerms);
|
|
51
85
|
return nowMs - offset;
|
|
52
86
|
}
|
|
53
87
|
const timestamp = Date.parse(value);
|
|
54
88
|
if (!Number.isFinite(timestamp) || new Date(timestamp).toISOString() !== value) {
|
|
55
|
-
invalidFilter(`timestamp '${value}' must be canonical ISO-8601 or now-<duration
|
|
89
|
+
invalidFilter(`timestamp '${value}' must be canonical ISO-8601 or now-<duration>`, recoveredTerms);
|
|
56
90
|
}
|
|
57
91
|
return timestamp;
|
|
58
92
|
}
|
|
59
|
-
function validateFilterValues(field, values) {
|
|
93
|
+
function validateFilterValues(field, values, recoveredTerms) {
|
|
94
|
+
if (field === "title")
|
|
95
|
+
return;
|
|
60
96
|
if (field === "state") {
|
|
61
97
|
for (const value of values)
|
|
62
98
|
if (!DISPLAY_VALUES.has(value))
|
|
63
|
-
invalidFilter(`unknown state '${value}'
|
|
99
|
+
invalidFilter(`unknown state '${value}'`, recoveredTerms);
|
|
64
100
|
return;
|
|
65
101
|
}
|
|
66
102
|
if (field === "priority") {
|
|
67
103
|
for (const value of values)
|
|
68
104
|
if (!/^[0-3]$/.test(value))
|
|
69
|
-
invalidFilter(`priority '${value}' must be 0 through 3
|
|
105
|
+
invalidFilter(`priority '${value}' must be 0 through 3`, recoveredTerms);
|
|
70
106
|
return;
|
|
71
107
|
}
|
|
72
108
|
if (field === "created" || field === "updated") {
|
|
73
109
|
for (const value of values)
|
|
74
|
-
parseTimestamp(value, 0);
|
|
110
|
+
parseTimestamp(value, 0, recoveredTerms);
|
|
75
111
|
return;
|
|
76
112
|
}
|
|
77
113
|
if (field === "parent" || field === "needs" || field === "blocks") {
|
|
@@ -79,14 +115,14 @@ function validateFilterValues(field, values) {
|
|
|
79
115
|
if (field === "parent" && value === "none")
|
|
80
116
|
continue;
|
|
81
117
|
if (!isTaskId(value))
|
|
82
|
-
invalidFilter(`value '${value}' must be a canonical full task id
|
|
118
|
+
invalidFilter(`value '${value}' must be a canonical full task id`, recoveredTerms);
|
|
83
119
|
}
|
|
84
120
|
return;
|
|
85
121
|
}
|
|
86
122
|
if (field === "contract") {
|
|
87
123
|
for (const value of values) {
|
|
88
124
|
if (value !== "none" && !isCommissionId(value))
|
|
89
|
-
invalidFilter(`value '${value}' must be a full commission id or none
|
|
125
|
+
invalidFilter(`value '${value}' must be a full commission id or none`, recoveredTerms);
|
|
90
126
|
}
|
|
91
127
|
}
|
|
92
128
|
}
|
|
@@ -114,10 +150,16 @@ function compareValue(left, right, operator) {
|
|
|
114
150
|
case "<=": return left <= right;
|
|
115
151
|
case ">": return left > right;
|
|
116
152
|
case ">=": return left >= right;
|
|
153
|
+
case "~": return false;
|
|
117
154
|
}
|
|
118
155
|
}
|
|
119
156
|
function matchesFilter(task, filter, nowMs) {
|
|
120
157
|
return filter.values.some((raw) => {
|
|
158
|
+
if (filter.field === "title") {
|
|
159
|
+
if (filter.operator === "~")
|
|
160
|
+
return task.title.toLowerCase().includes(raw.toLowerCase());
|
|
161
|
+
return compareValue(task.title, raw, filter.operator);
|
|
162
|
+
}
|
|
121
163
|
if (filter.field === "state")
|
|
122
164
|
return compareValue(deriveTaskDisplay(task), raw, filter.operator);
|
|
123
165
|
if (filter.field === "priority")
|
|
@@ -167,6 +209,6 @@ export function selectTaskList(tasks, options = {}) {
|
|
|
167
209
|
const nowMs = options.nowMs ?? Date.now();
|
|
168
210
|
return tasks
|
|
169
211
|
.filter((task) => selected(task, options.selection ?? "default"))
|
|
170
|
-
.filter((task) => filters.every((filter) => matchesFilter(task,
|
|
212
|
+
.filter((task) => filters.every((filter) => filter.some((term) => matchesFilter(task, term, nowMs))))
|
|
171
213
|
.sort(sort === undefined ? defaultCompare : (left, right) => explicitCompare(sort, left, right));
|
|
172
214
|
}
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
// Generated into build output by scripts/build.mjs
|
|
2
|
-
export const VERSION = "2.9.
|
|
3
|
-
export const GIT_HASH = "
|
|
2
|
+
export const VERSION = "2.9.6";
|
|
3
|
+
export const GIT_HASH = "cb7f9ef";
|
package/package.json
CHANGED
|
@@ -67,6 +67,12 @@ means it constructed the report, not that the candidate is acceptable. Use
|
|
|
67
67
|
Commissioned workers never write Git metadata. The host alone reviews and
|
|
68
68
|
commits accepted bytes before `arc`, `renew`, or `petition`.
|
|
69
69
|
|
|
70
|
+
Dispatch rule: every time a dispatcher sends a worker, whether through
|
|
71
|
+
`keiyaku call` or any other agent tool, the prompt must explicitly tell that
|
|
72
|
+
worker to read the worktree-root `.keiyaku/KEIYAKU.md` first and follow its
|
|
73
|
+
objective, scope, and checks. The file is the contract-body carrier; do not
|
|
74
|
+
copy its rendered body into a dispatch prompt.
|
|
75
|
+
|
|
70
76
|
## Mid-flight commands (use when needed, skip otherwise)
|
|
71
77
|
|
|
72
78
|
```bash
|