killeros 2.0.8 → 2.0.10
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/CHANGELOG.md +13 -0
- package/Killeros.ts +11 -4
- package/README.md +147 -134
- package/killeros/auto-compaction.ts +224 -0
- package/killeros/commands.ts +108 -34
- package/killeros/goals.ts +75 -2
- package/killeros/notifications.ts +6 -36
- package/killeros/runtime.ts +2 -0
- package/killeros/settings.ts +47 -0
- package/killeros/shell-ui.ts +109 -5
- package/killeros/workflow-gate.ts +35 -17
- package/package.json +1 -1
|
@@ -19,7 +19,10 @@ export interface WorkflowPolicy {
|
|
|
19
19
|
|
|
20
20
|
export interface WorkflowAdapter {
|
|
21
21
|
id: string;
|
|
22
|
-
activation
|
|
22
|
+
/** The existing single explicit skill activation API. */
|
|
23
|
+
activation?: string;
|
|
24
|
+
/** Additional explicit skill activations that share this adapter policy. */
|
|
25
|
+
activations?: readonly string[];
|
|
23
26
|
question: QuestionParamsValue;
|
|
24
27
|
policies: readonly WorkflowPolicy[];
|
|
25
28
|
selectPolicy: (details: QuestionDetails) => WorkflowPolicy | undefined;
|
|
@@ -48,11 +51,14 @@ type InternalState =
|
|
|
48
51
|
| {
|
|
49
52
|
kind: "pending_decision";
|
|
50
53
|
adapter: WorkflowAdapter;
|
|
54
|
+
activation: string;
|
|
51
55
|
abortController: AbortController;
|
|
52
56
|
token: symbol;
|
|
53
57
|
}
|
|
54
|
-
| { kind: "active"; adapter: WorkflowAdapter; policy: WorkflowPolicy; token: symbol }
|
|
55
|
-
| { kind: "terminal_cleanup"; adapter: WorkflowAdapter; reason: WorkflowTerminalReason; token: symbol };
|
|
58
|
+
| { kind: "active"; adapter: WorkflowAdapter; activation: string; policy: WorkflowPolicy; token: symbol }
|
|
59
|
+
| { kind: "terminal_cleanup"; adapter: WorkflowAdapter; activation: string; reason: WorkflowTerminalReason; token: symbol };
|
|
60
|
+
|
|
61
|
+
const ACTIVATION_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._-]*$/u;
|
|
56
62
|
|
|
57
63
|
function explicitSkillActivation(text: string): string | undefined {
|
|
58
64
|
if (!text.startsWith("/skill:")) return;
|
|
@@ -76,21 +82,21 @@ function publicState(state: InternalState): WorkflowGateState {
|
|
|
76
82
|
return {
|
|
77
83
|
kind: state.kind,
|
|
78
84
|
adapterId: state.adapter.id,
|
|
79
|
-
activation: state.
|
|
85
|
+
activation: state.activation,
|
|
80
86
|
};
|
|
81
87
|
}
|
|
82
88
|
if (state.kind === "terminal_cleanup") {
|
|
83
89
|
return {
|
|
84
90
|
kind: state.kind,
|
|
85
91
|
adapterId: state.adapter.id,
|
|
86
|
-
activation: state.
|
|
92
|
+
activation: state.activation,
|
|
87
93
|
reason: state.reason,
|
|
88
94
|
};
|
|
89
95
|
}
|
|
90
96
|
return {
|
|
91
97
|
kind: state.kind,
|
|
92
98
|
adapterId: state.adapter.id,
|
|
93
|
-
activation: state.
|
|
99
|
+
activation: state.activation,
|
|
94
100
|
policyId: state.policy.id,
|
|
95
101
|
};
|
|
96
102
|
}
|
|
@@ -141,14 +147,26 @@ export function registerWorkflowGate(
|
|
|
141
147
|
const adaptersByActivation = new Map<string, WorkflowAdapter>();
|
|
142
148
|
for (const adapter of adapters) {
|
|
143
149
|
if (!adapter.id.trim()) throw new Error("Decision-gated workflow adapters require an id");
|
|
144
|
-
if (
|
|
145
|
-
throw new Error(`
|
|
150
|
+
if (adapter.activations !== undefined && !Array.isArray(adapter.activations)) {
|
|
151
|
+
throw new Error(`Workflow adapter ${adapter.id} activations must be an array`);
|
|
146
152
|
}
|
|
147
|
-
|
|
148
|
-
|
|
153
|
+
const activations = [
|
|
154
|
+
...(adapter.activation === undefined ? [] : [adapter.activation]),
|
|
155
|
+
...(adapter.activations ?? []),
|
|
156
|
+
];
|
|
157
|
+
if (activations.length === 0) {
|
|
158
|
+
throw new Error(`Workflow adapter ${adapter.id} must register at least one explicit skill activation`);
|
|
159
|
+
}
|
|
160
|
+
for (const activation of activations) {
|
|
161
|
+
if (typeof activation !== "string" || !ACTIVATION_PATTERN.test(activation)) {
|
|
162
|
+
throw new Error(`Invalid decision-gated workflow activation: ${activation}`);
|
|
163
|
+
}
|
|
164
|
+
if (adaptersByActivation.has(activation)) {
|
|
165
|
+
throw new Error(`Duplicate decision-gated workflow activation: ${activation}`);
|
|
166
|
+
}
|
|
167
|
+
adaptersByActivation.set(activation, adapter);
|
|
149
168
|
}
|
|
150
169
|
if (adapter.policies.length === 0) throw new Error(`Workflow adapter ${adapter.id} has no policies`);
|
|
151
|
-
adaptersByActivation.set(adapter.activation, adapter);
|
|
152
170
|
}
|
|
153
171
|
|
|
154
172
|
let state: InternalState = { kind: "inactive" };
|
|
@@ -160,6 +178,7 @@ export function registerWorkflowGate(
|
|
|
160
178
|
state = {
|
|
161
179
|
kind: "terminal_cleanup",
|
|
162
180
|
adapter: current.adapter,
|
|
181
|
+
activation: current.activation,
|
|
163
182
|
reason,
|
|
164
183
|
token: current.token,
|
|
165
184
|
};
|
|
@@ -204,13 +223,12 @@ export function registerWorkflowGate(
|
|
|
204
223
|
};
|
|
205
224
|
|
|
206
225
|
pi.on("input", async (event, ctx) => {
|
|
207
|
-
|
|
208
|
-
if (!activation) return;
|
|
209
|
-
|
|
210
|
-
if (state.kind === "pending_decision" || state.kind === "terminal_cleanup") {
|
|
226
|
+
if ((state.kind === "pending_decision" || state.kind === "terminal_cleanup") && event.text.startsWith("/skill:")) {
|
|
211
227
|
notify(ctx, "A decision-gated workflow is waiting for its structured question; skill routing is blocked", "warning");
|
|
212
228
|
return { action: "handled" };
|
|
213
229
|
}
|
|
230
|
+
const activation = explicitSkillActivation(event.text);
|
|
231
|
+
if (!activation) return;
|
|
214
232
|
|
|
215
233
|
const adapter = adaptersByActivation.get(activation);
|
|
216
234
|
if (!adapter) return;
|
|
@@ -230,7 +248,7 @@ export function registerWorkflowGate(
|
|
|
230
248
|
|
|
231
249
|
const abortController = new AbortController();
|
|
232
250
|
const token = Symbol();
|
|
233
|
-
state = { kind: "pending_decision", adapter, abortController, token };
|
|
251
|
+
state = { kind: "pending_decision", adapter, activation, abortController, token };
|
|
234
252
|
let details: QuestionDetails;
|
|
235
253
|
try {
|
|
236
254
|
details = await questionRunner.ask(adapter.question, abortController.signal, ctx);
|
|
@@ -275,7 +293,7 @@ export function registerWorkflowGate(
|
|
|
275
293
|
if (state.kind !== "pending_decision" || state.adapter !== adapter || state.token !== token) {
|
|
276
294
|
return { action: "handled" };
|
|
277
295
|
}
|
|
278
|
-
state = { kind: "active", adapter, policy, token };
|
|
296
|
+
state = { kind: "active", adapter, activation, policy, token };
|
|
279
297
|
return { action: "continue" };
|
|
280
298
|
});
|
|
281
299
|
|