blun-king-cli 9.1.419 → 9.1.421
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/LIESMICH.txt +16 -0
- package/README.md +16 -0
- package/bin/cognitive-action-checkpoint.cjs +18 -5
- package/bin/cognitive-goal-autostart-policy.cjs +26 -5
- package/blun.mjs +29 -7
- package/package.json +1 -1
package/LIESMICH.txt
CHANGED
|
@@ -90,6 +90,22 @@ erscheint nicht als erfundene Benutzernachricht und wird pro Sitzung nur einmal
|
|
|
90
90
|
angestoßen. Wartende, pausierte und blockierte Ziele sowie manuelle
|
|
91
91
|
Berechtigungsmodi starten weiterhin nicht selbstständig.
|
|
92
92
|
|
|
93
|
+
Ab BLUN King 9.1.420 beendet ein aktives Ziel mit dauerhaftem
|
|
94
|
+
Warte-Checkpoint die autonome Fortsetzung nach dem aktuellen Zug, ohne das Ziel
|
|
95
|
+
zu verwerfen. Externe, zeitliche, abhängige und nutzerabhängige Auslöser
|
|
96
|
+
erzeugen dadurch keine leeren Folgezüge. Beim nächsten Ereignis wird der
|
|
97
|
+
gespeicherte Auslöser erneut eingeordnet. Sofortige Ziele und ältere Ziele ohne
|
|
98
|
+
Checkpoint laufen unverändert weiter.
|
|
99
|
+
|
|
100
|
+
Ab BLUN King 9.1.421 muss ein wartendes Ziel mit Zeit-Trigger einen genauen
|
|
101
|
+
`dueAt`-Zeitpunkt speichern. Der Zeitpunkt wird auf UTC normalisiert. Bei einem
|
|
102
|
+
natürlichen Sitzungsstart im Auto- oder God-Modus wartet das Ziel vor diesem
|
|
103
|
+
Zeitpunkt weiter und startet, sobald der Zeitpunkt erreicht oder überschritten
|
|
104
|
+
ist. Der erste Fortsetzungszug erhält die gespeicherte Fälligkeit als
|
|
105
|
+
ausdrücklichen Trigger-Beleg. Andere Trigger-Arten dürfen kein `dueAt`
|
|
106
|
+
enthalten. Ein Laufzeit-Wecker für eine durchgehend geöffnete Sitzung ist in
|
|
107
|
+
diesem Release noch nicht enthalten.
|
|
108
|
+
|
|
93
109
|
Zuverlässiger King-Start
|
|
94
110
|
-----------------------
|
|
95
111
|
Bei einer ausdrücklich als wiederholbar gekennzeichneten Serverüberlastung
|
package/README.md
CHANGED
|
@@ -109,6 +109,22 @@ erscheint nicht als erfundene Benutzernachricht und wird pro Sitzung nur einmal
|
|
|
109
109
|
angestoßen. Wartende, pausierte und blockierte Ziele sowie manuelle
|
|
110
110
|
Berechtigungsmodi starten weiterhin nicht selbstständig.
|
|
111
111
|
|
|
112
|
+
Ab BLUN King 9.1.420 beendet ein aktives Ziel mit dauerhaftem
|
|
113
|
+
Warte-Checkpoint die autonome Fortsetzung nach dem aktuellen Zug, ohne das Ziel
|
|
114
|
+
zu verwerfen. Externe, zeitliche, abhängige und nutzerabhängige Auslöser
|
|
115
|
+
erzeugen dadurch keine leeren Folgezüge. Beim nächsten Ereignis wird der
|
|
116
|
+
gespeicherte Auslöser erneut eingeordnet. Sofortige Ziele und ältere Ziele ohne
|
|
117
|
+
Checkpoint laufen unverändert weiter.
|
|
118
|
+
|
|
119
|
+
Ab BLUN King 9.1.421 muss ein wartendes Ziel mit Zeit-Trigger einen genauen
|
|
120
|
+
`dueAt`-Zeitpunkt speichern. Der Zeitpunkt wird auf UTC normalisiert. Bei einem
|
|
121
|
+
natürlichen Sitzungsstart im Auto- oder God-Modus wartet das Ziel vor diesem
|
|
122
|
+
Zeitpunkt weiter und startet, sobald der Zeitpunkt erreicht oder überschritten
|
|
123
|
+
ist. Der erste Fortsetzungszug erhält die gespeicherte Fälligkeit als
|
|
124
|
+
ausdrücklichen Trigger-Beleg. Andere Trigger-Arten dürfen kein `dueAt`
|
|
125
|
+
enthalten. Ein Laufzeit-Wecker für eine durchgehend geöffnete Sitzung ist in
|
|
126
|
+
diesem Release noch nicht enthalten.
|
|
127
|
+
|
|
112
128
|
## Zuverlässiger King-Start
|
|
113
129
|
|
|
114
130
|
Bei einer vom Server ausdrücklich als wiederholbar gekennzeichneten
|
|
@@ -20,11 +20,12 @@ const PROBLEM_FRAME_KEYS = new Set([
|
|
|
20
20
|
'successCriterion', 'missingKnowledge', 'candidateActions', 'selectedAction',
|
|
21
21
|
'selectionReason', 'supportChoice', 'risk', 'reversibility',
|
|
22
22
|
]);
|
|
23
|
-
const NEXT_TRIGGER_KEYS = new Set(['kind', 'condition']);
|
|
23
|
+
const NEXT_TRIGGER_KEYS = new Set(['kind', 'condition', 'dueAt']);
|
|
24
24
|
const EVIDENCE_INPUT_KEYS = new Set([
|
|
25
25
|
'turnId', 'toolCallId', 'toolName', 'decision', 'outcome', 'durationMs',
|
|
26
26
|
]);
|
|
27
27
|
const EVIDENCE_DIGEST_RE = /^[a-f0-9]{16}$/u;
|
|
28
|
+
const ISO_TIMESTAMP_RE = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{1,9})?(?:Z|[+-]\d{2}(?::?\d{2})?)$/u;
|
|
28
29
|
|
|
29
30
|
function bounded(value, field, max = 512) {
|
|
30
31
|
const text = String(value ?? '')
|
|
@@ -35,9 +36,12 @@ function bounded(value, field, max = 512) {
|
|
|
35
36
|
return text.length <= max ? text : `${text.slice(0, max - 3).trimEnd()}...`;
|
|
36
37
|
}
|
|
37
38
|
|
|
38
|
-
function normalizedTimestamp(value) {
|
|
39
|
+
function normalizedTimestamp(value, field = 'updatedAt', requireIsoString = false) {
|
|
40
|
+
if (requireIsoString && (typeof value !== 'string' || !ISO_TIMESTAMP_RE.test(value))) {
|
|
41
|
+
throw new TypeError(`${field} must be an ISO timestamp`);
|
|
42
|
+
}
|
|
39
43
|
const date = new Date(value);
|
|
40
|
-
if (!Number.isFinite(date.getTime())) throw new TypeError(
|
|
44
|
+
if (!Number.isFinite(date.getTime())) throw new TypeError(`${field} must be an ISO timestamp`);
|
|
41
45
|
return date.toISOString();
|
|
42
46
|
}
|
|
43
47
|
|
|
@@ -96,10 +100,18 @@ function normalizeNextTrigger(input, phase) {
|
|
|
96
100
|
if (phase !== 'wait' && kind !== 'immediate') {
|
|
97
101
|
throw new TypeError('non-wait phase requires an immediate nextTrigger');
|
|
98
102
|
}
|
|
99
|
-
|
|
103
|
+
if (kind === 'time' && input.dueAt === undefined) {
|
|
104
|
+
throw new TypeError('time nextTrigger requires dueAt');
|
|
105
|
+
}
|
|
106
|
+
if (kind !== 'time' && input.dueAt !== undefined) {
|
|
107
|
+
throw new TypeError('dueAt is only valid for a time nextTrigger');
|
|
108
|
+
}
|
|
109
|
+
const trigger = {
|
|
100
110
|
kind,
|
|
101
111
|
condition: bounded(input.condition, 'nextTrigger condition'),
|
|
102
|
-
}
|
|
112
|
+
};
|
|
113
|
+
if (kind === 'time') trigger.dueAt = normalizedTimestamp(input.dueAt, 'nextTrigger dueAt', true);
|
|
114
|
+
return Object.freeze(trigger);
|
|
103
115
|
}
|
|
104
116
|
|
|
105
117
|
function normalizedEvidenceBasis(value, allowLegacy = false) {
|
|
@@ -287,6 +299,7 @@ function projectActionCheckpoint(checkpoint) {
|
|
|
287
299
|
lines.push(`Expected evidence: ${value.expectedEvidence}`);
|
|
288
300
|
if (value.nextTrigger !== undefined) {
|
|
289
301
|
lines.push(`Next trigger: ${value.nextTrigger.kind.replaceAll('_', ' ')} - ${value.nextTrigger.condition}`);
|
|
302
|
+
if (value.nextTrigger.dueAt !== undefined) lines.push(`Due at: ${value.nextTrigger.dueAt}`);
|
|
290
303
|
}
|
|
291
304
|
if (value.problemFrame !== undefined) {
|
|
292
305
|
const frame = value.problemFrame;
|
|
@@ -2,21 +2,42 @@
|
|
|
2
2
|
|
|
3
3
|
const AUTONOMOUS_PERMISSION_MODES = new Set(['auto', 'yolo']);
|
|
4
4
|
const ACTIVE_PHASES = new Set(['orient', 'plan', 'act', 'verify', 'learn']);
|
|
5
|
+
const ISO_TIMESTAMP_RE = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{1,9})?(?:Z|[+-]\d{2}(?::?\d{2})?)$/u;
|
|
5
6
|
const START = Object.freeze({ kind: 'start', trigger: 'immediate' });
|
|
6
7
|
const WAIT = Object.freeze({ kind: 'wait' });
|
|
8
|
+
const CONTINUE = Object.freeze({ kind: 'continue' });
|
|
9
|
+
const YIELD = Object.freeze({ kind: 'yield' });
|
|
7
10
|
|
|
8
|
-
function goalAutostartDecision({ goal, permissionMode } = {}) {
|
|
11
|
+
function goalAutostartDecision({ goal, permissionMode, now = new Date() } = {}) {
|
|
9
12
|
if (!goal || typeof goal !== 'object' || Array.isArray(goal)) return WAIT;
|
|
10
13
|
if (goal.status !== 'active' || !AUTONOMOUS_PERMISSION_MODES.has(permissionMode)) return WAIT;
|
|
11
14
|
|
|
12
15
|
const checkpoint = goal.actionCheckpoint;
|
|
13
16
|
if (!checkpoint || typeof checkpoint !== 'object' || Array.isArray(checkpoint)) return WAIT;
|
|
14
|
-
if (!ACTIVE_PHASES.has(checkpoint.phase)) return WAIT;
|
|
15
17
|
const trigger = checkpoint.nextTrigger;
|
|
16
18
|
if (!trigger || typeof trigger !== 'object' || Array.isArray(trigger)) return WAIT;
|
|
17
|
-
if (trigger.kind !== 'immediate') return WAIT;
|
|
18
19
|
if (typeof trigger.condition !== 'string' || trigger.condition.trim().length === 0) return WAIT;
|
|
19
|
-
return START;
|
|
20
|
+
if (ACTIVE_PHASES.has(checkpoint.phase) && trigger.kind === 'immediate') return START;
|
|
21
|
+
if (checkpoint.phase !== 'wait' || trigger.kind !== 'time') return WAIT;
|
|
22
|
+
|
|
23
|
+
const dueAt = String(trigger.dueAt ?? '');
|
|
24
|
+
if (!ISO_TIMESTAMP_RE.test(dueAt)) return WAIT;
|
|
25
|
+
const dueAtMs = new Date(dueAt).getTime();
|
|
26
|
+
const nowMs = new Date(now).getTime();
|
|
27
|
+
if (!Number.isFinite(dueAtMs) || !Number.isFinite(nowMs) || dueAtMs > nowMs) return WAIT;
|
|
28
|
+
return Object.freeze({
|
|
29
|
+
kind: 'start',
|
|
30
|
+
trigger: 'time',
|
|
31
|
+
dueAt,
|
|
32
|
+
observation: `The checkpointed time trigger became due at ${dueAt}.`,
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function goalContinuationDecision({ goal } = {}) {
|
|
37
|
+
if (!goal || typeof goal !== 'object' || Array.isArray(goal) || goal.status !== 'active') return YIELD;
|
|
38
|
+
const checkpoint = goal.actionCheckpoint;
|
|
39
|
+
if (!checkpoint || typeof checkpoint !== 'object' || Array.isArray(checkpoint)) return CONTINUE;
|
|
40
|
+
return checkpoint.phase === 'wait' ? YIELD : CONTINUE;
|
|
20
41
|
}
|
|
21
42
|
|
|
22
|
-
module.exports = { goalAutostartDecision };
|
|
43
|
+
module.exports = { goalAutostartDecision, goalContinuationDecision };
|
package/blun.mjs
CHANGED
|
@@ -37,7 +37,7 @@ import * as win32Path from "node:path/win32";
|
|
|
37
37
|
import { createServer } from "node:http";
|
|
38
38
|
import { pipeline as pipeline$1 } from "node:stream/promises";
|
|
39
39
|
const { writeTelegramConsoleStatus } = telegramConsoleStatusPolicy;
|
|
40
|
-
const { goalAutostartDecision } = cognitiveGoalAutostartPolicy;
|
|
40
|
+
const { goalAutostartDecision, goalContinuationDecision } = cognitiveGoalAutostartPolicy;
|
|
41
41
|
import { EventEmitter as EventEmitter$1 } from "node:events";
|
|
42
42
|
import { StringDecoder } from "node:string_decoder";
|
|
43
43
|
import co from "node:assert";
|
|
@@ -245769,7 +245769,8 @@ var init_events$1 = __esmMin((() => {
|
|
|
245769
245769
|
expectedEvidence: string(),
|
|
245770
245770
|
nextTrigger: object({
|
|
245771
245771
|
kind: _enum(["immediate", "external_event", "time", "dependency", "user_decision"]),
|
|
245772
|
-
condition: string()
|
|
245772
|
+
condition: string(),
|
|
245773
|
+
dueAt: string().optional()
|
|
245773
245774
|
}).strict().optional(),
|
|
245774
245775
|
problemFrame: object({
|
|
245775
245776
|
successCriterion: string(),
|
|
@@ -260288,8 +260289,26 @@ function createActionCheckpointInputSchema(problemFrameSchema, requireProblemFra
|
|
|
260288
260289
|
expectedEvidence: string().min(1).max(512),
|
|
260289
260290
|
nextTrigger: object({
|
|
260290
260291
|
kind: _enum(["immediate", "external_event", "time", "dependency", "user_decision"]),
|
|
260291
|
-
condition: string().min(1).max(512)
|
|
260292
|
-
|
|
260292
|
+
condition: string().min(1).max(512),
|
|
260293
|
+
dueAt: string().min(1).max(64).optional()
|
|
260294
|
+
}).strict().superRefine((value, ctx) => {
|
|
260295
|
+
if (value.kind === "time") {
|
|
260296
|
+
if (value.dueAt === void 0) ctx.addIssue({
|
|
260297
|
+
code: "custom",
|
|
260298
|
+
path: ["dueAt"],
|
|
260299
|
+
message: "time nextTrigger requires dueAt"
|
|
260300
|
+
});
|
|
260301
|
+
else if (!/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{1,9})?(?:Z|[+-]\d{2}(?::?\d{2})?)$/u.test(value.dueAt) || !Number.isFinite(new Date(value.dueAt).getTime())) ctx.addIssue({
|
|
260302
|
+
code: "custom",
|
|
260303
|
+
path: ["dueAt"],
|
|
260304
|
+
message: "dueAt must be an ISO timestamp"
|
|
260305
|
+
});
|
|
260306
|
+
} else if (value.dueAt !== void 0) ctx.addIssue({
|
|
260307
|
+
code: "custom",
|
|
260308
|
+
path: ["dueAt"],
|
|
260309
|
+
message: "dueAt is only valid for a time nextTrigger"
|
|
260310
|
+
});
|
|
260311
|
+
}),
|
|
260293
260312
|
problemFrame: requireProblemFrame ? problemFrameSchema : problemFrameSchema.optional()
|
|
260294
260313
|
}).strict();
|
|
260295
260314
|
}
|
|
@@ -262100,6 +262119,7 @@ var init_turn = __esmMin((() => {
|
|
|
262100
262119
|
await this.agent.goal.markBlocked({ reason: "A configured budget was reached" });
|
|
262101
262120
|
return end;
|
|
262102
262121
|
}
|
|
262122
|
+
if (goalContinuationDecision({ goal }).kind === "yield") return end;
|
|
262103
262123
|
turnId = this.allocateTurnId();
|
|
262104
262124
|
turnInput = [{
|
|
262105
262125
|
type: "text",
|
|
@@ -262781,7 +262801,7 @@ var init_outcome_prompts = __esmMin((() => {}));
|
|
|
262781
262801
|
//#region ../../packages/agent-core/src/tools/builtin/goal/update-goal.md?raw
|
|
262782
262802
|
var update_goal_default;
|
|
262783
262803
|
var init_update_goal$1 = __esmMin((() => {
|
|
262784
|
-
update_goal_default = "Update the current autonomous goal. Set `status` only for a lifecycle change. After a coherent work slice, save `actionCheckpoint` with a monotone revision, the last verified result, exact next action, expected evidence, exact `nextTrigger`, and an explicit evidence basis. Persist the exact `nextTrigger` that releases `nextAction`: use `immediate` outside the `wait` phase; while waiting, name the external event, time, dependency, or user decision instead of pretending work can continue. Use `runtime_tool` only when a successful tool in this turn measured the result; use `user_statement` for a direct user assertion, `external_report` for a report not independently measured here, and `carried_forward` only when the last verified text is unchanged. Classify knowledge as `verified`, `credible_unverified`, `hypothesis`, `uncertain_memory`, `stale`, or `unknown`; never present a weaker state as verified, and preserve the state on carry-forward. Start at revision 1 and increment the currently projected revision by exactly one; stale writers fail closed. This is durable progress state, not permission, and should change only when the facts change. A checkpoint-only call keeps the goal active.\n\n- `active` — resume a paused or blocked goal when the user explicitly asks you to work on that goal.\n- `complete` — the objective is fully satisfied, all files are written, all tests pass, and any stated validation has passed. When the goal has a completion criterion, first save a `verify` checkpoint with `runtime_tool`, `verified`, and a successful runtime evidence receipt.\n- `blocked` — a genuine external condition or required user decision prevents progress.\n- `paused` — set the goal aside for now.\n\nDo not mark complete after a plan or partial result. If useful work remains, checkpoint it and continue. Do not ask for permission merely to execute an already authorized checkpoint; ask only at a real rights boundary or missing user decision.\n";
|
|
262804
|
+
update_goal_default = "Update the current autonomous goal. Set `status` only for a lifecycle change. After a coherent work slice, save `actionCheckpoint` with a monotone revision, the last verified result, exact next action, expected evidence, exact `nextTrigger`, and an explicit evidence basis. Persist the exact `nextTrigger` that releases `nextAction`: use `immediate` outside the `wait` phase; while waiting, name the external event, time, dependency, or user decision instead of pretending work can continue. A `time` trigger must include the exact ISO timestamp in `dueAt`; no other trigger kind may include `dueAt`. Use `runtime_tool` only when a successful tool in this turn measured the result; use `user_statement` for a direct user assertion, `external_report` for a report not independently measured here, and `carried_forward` only when the last verified text is unchanged. Classify knowledge as `verified`, `credible_unverified`, `hypothesis`, `uncertain_memory`, `stale`, or `unknown`; never present a weaker state as verified, and preserve the state on carry-forward. Start at revision 1 and increment the currently projected revision by exactly one; stale writers fail closed. This is durable progress state, not permission, and should change only when the facts change. A checkpoint-only call keeps the goal active.\n\n- `active` — resume a paused or blocked goal when the user explicitly asks you to work on that goal.\n- `complete` — the objective is fully satisfied, all files are written, all tests pass, and any stated validation has passed. When the goal has a completion criterion, first save a `verify` checkpoint with `runtime_tool`, `verified`, and a successful runtime evidence receipt.\n- `blocked` — a genuine external condition or required user decision prevents progress.\n- `paused` — set the goal aside for now.\n\nDo not mark complete after a plan or partial result. If useful work remains, checkpoint it and continue. Do not ask for permission merely to execute an already authorized checkpoint; ask only at a real rights boundary or missing user decision.\n";
|
|
262785
262805
|
update_goal_default += "\nFor a non-trivial or unfamiliar problem, preserve `problemFrame` with the success criterion, missing knowledge, bounded candidate actions, selected action and reason, support choice, risk, and reversibility. The selected action must match one candidate. Problem framing is descriptive state and never grants permission.\n";
|
|
262786
262806
|
}));
|
|
262787
262807
|
//#endregion
|
|
@@ -516672,9 +516692,11 @@ var BlunTUI = class {
|
|
|
516672
516692
|
const sessionId = session.id;
|
|
516673
516693
|
const autostart = goalAutostartDecision({
|
|
516674
516694
|
goal,
|
|
516675
|
-
permissionMode: this.state.appState.permissionMode
|
|
516695
|
+
permissionMode: this.state.appState.permissionMode,
|
|
516696
|
+
now: new Date()
|
|
516676
516697
|
});
|
|
516677
516698
|
if (autostart.kind === "start") {
|
|
516699
|
+
const autostartPrompt = autostart.observation === void 0 ? GOAL_CONTINUATION_PROMPT : `${GOAL_CONTINUATION_PROMPT}\n\n${autostart.observation}`;
|
|
516678
516700
|
this.startupGoalPromptedSessionId = sessionId;
|
|
516679
516701
|
this.beginSessionRequest();
|
|
516680
516702
|
this.setAppState({
|
|
@@ -516682,7 +516704,7 @@ var BlunTUI = class {
|
|
|
516682
516704
|
modelFallbackAllowed: false
|
|
516683
516705
|
});
|
|
516684
516706
|
try {
|
|
516685
|
-
const result = await session.promptAccepted(
|
|
516707
|
+
const result = await session.promptAccepted(autostartPrompt);
|
|
516686
516708
|
if (!result.accepted && this.session?.id === sessionId) {
|
|
516687
516709
|
this.setAppState({ streamingPhase: "idle" });
|
|
516688
516710
|
this.resetLivePane();
|