@kuralle-agents/cli 0.14.0 → 0.15.0
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/fileStore.js +12 -2
- package/dist/send.js +40 -4
- package/package.json +9 -7
package/dist/fileStore.js
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
8
8
|
import { dirname } from 'node:path';
|
|
9
|
-
import { reviveSession } from '@kuralle-agents/core';
|
|
9
|
+
import { reviveSession, StaleWriteError } from '@kuralle-agents/core';
|
|
10
10
|
export function fileSessionStore(path) {
|
|
11
11
|
const readAll = () => {
|
|
12
12
|
if (!existsSync(path))
|
|
@@ -28,8 +28,18 @@ export function fileSessionStore(path) {
|
|
|
28
28
|
return map[id] ? reviveSession(map[id]) : null;
|
|
29
29
|
},
|
|
30
30
|
async save(session) {
|
|
31
|
+
// Compare-and-swap, matching MemoryStore. The durable journal appends through
|
|
32
|
+
// `mutateSessionWithRetry`, which retries on StaleWriteError — a store that accepts
|
|
33
|
+
// a stale write silently drops the losing append instead, and the step it wrote is
|
|
34
|
+
// then missing when finalizeStep looks for it.
|
|
31
35
|
const map = readAll();
|
|
32
|
-
map[session.id]
|
|
36
|
+
const existing = map[session.id];
|
|
37
|
+
const expected = session.version ?? 0;
|
|
38
|
+
const stored = existing ? (existing.version ?? 0) : 0;
|
|
39
|
+
if (stored !== expected) {
|
|
40
|
+
throw new StaleWriteError(session.id, expected, stored);
|
|
41
|
+
}
|
|
42
|
+
map[session.id] = { ...session, updatedAt: new Date(), version: expected + 1 };
|
|
33
43
|
writeAll(map);
|
|
34
44
|
},
|
|
35
45
|
async delete(id) {
|
package/dist/send.js
CHANGED
|
@@ -2,6 +2,11 @@
|
|
|
2
2
|
* send — ONE turn against a PERSISTED session for adaptive multi-turn conversations.
|
|
3
3
|
*
|
|
4
4
|
* Flags: --session <id> · --store <file> · --state · --reset
|
|
5
|
+
* --approve [by] · --deny [by] · --signal <name> [--payload <json>]
|
|
6
|
+
*
|
|
7
|
+
* A `needsApproval` tool suspends the run durably. Without a way to deliver the decision the
|
|
8
|
+
* CLI could enter that pause and never leave it — every later turn re-requested approval and
|
|
9
|
+
* paused again. The runtime already accepted `signalDelivery`; only the CLI could not send one.
|
|
5
10
|
*/
|
|
6
11
|
import { join } from 'node:path';
|
|
7
12
|
import { fileSessionStore } from './fileStore.js';
|
|
@@ -15,9 +20,39 @@ export async function runSend(argv, buildRuntime) {
|
|
|
15
20
|
const storePath = flag(argv, '--store') ?? join(process.cwd(), 'runs/tui-sessions.json');
|
|
16
21
|
const doReset = argv.includes('--reset');
|
|
17
22
|
const doState = argv.includes('--state');
|
|
18
|
-
const
|
|
23
|
+
const approve = argv.includes('--approve');
|
|
24
|
+
const deny = argv.includes('--deny');
|
|
25
|
+
const signalName = flag(argv, '--signal');
|
|
26
|
+
const by = flag(argv, '--approve') ?? flag(argv, '--deny') ?? 'cli';
|
|
27
|
+
const signalDelivery = approve || deny
|
|
28
|
+
? {
|
|
29
|
+
// A fresh id per delivery: recordSignalDelivery dedupes on it, so reusing one
|
|
30
|
+
// would make a second decision a silent no-op.
|
|
31
|
+
signalId: `cli-${Date.now()}`,
|
|
32
|
+
name: '__approval',
|
|
33
|
+
payload: { approved: approve, by },
|
|
34
|
+
}
|
|
35
|
+
: signalName
|
|
36
|
+
? {
|
|
37
|
+
signalId: `cli-${Date.now()}`,
|
|
38
|
+
name: signalName,
|
|
39
|
+
payload: JSON.parse(flag(argv, '--payload') ?? '{}'),
|
|
40
|
+
}
|
|
41
|
+
: undefined;
|
|
42
|
+
// Every flag whose VALUE must not be mistaken for message text. Missing `--model` here
|
|
43
|
+
// meant the model id survived the filter and became the first word of every user turn —
|
|
44
|
+
// a whole live run was sent to the model with "gpt-4.1-mini " prepended to each message.
|
|
45
|
+
const consumesValue = new Set([
|
|
46
|
+
'--session',
|
|
47
|
+
'--store',
|
|
48
|
+
'--model',
|
|
49
|
+
'--signal',
|
|
50
|
+
'--payload',
|
|
51
|
+
'--approve',
|
|
52
|
+
'--deny',
|
|
53
|
+
]);
|
|
19
54
|
const message = argv
|
|
20
|
-
.filter((a, i) => !a.startsWith('--') && !(i > 0 && (argv[i - 1]
|
|
55
|
+
.filter((a, i) => !a.startsWith('--') && !(i > 0 && consumesValue.has(argv[i - 1])))
|
|
21
56
|
.join(' ')
|
|
22
57
|
.trim();
|
|
23
58
|
const store = fileSessionStore(storePath);
|
|
@@ -37,14 +72,15 @@ export async function runSend(argv, buildRuntime) {
|
|
|
37
72
|
console.log(`reset session "${sessionId}"`);
|
|
38
73
|
return;
|
|
39
74
|
}
|
|
40
|
-
|
|
75
|
+
// A decision can arrive with no message — approving is itself the turn.
|
|
76
|
+
if (doState || (!message && !signalDelivery)) {
|
|
41
77
|
console.log(await readState());
|
|
42
78
|
if (!message && !doState)
|
|
43
79
|
console.error('(no message — pass one to take a turn, or --state to inspect)');
|
|
44
80
|
return;
|
|
45
81
|
}
|
|
46
82
|
const events = [];
|
|
47
|
-
const handle = demo.runtime.run({ sessionId, input: message });
|
|
83
|
+
const handle = demo.runtime.run({ sessionId, input: message, ...(signalDelivery ? { signalDelivery } : {}) });
|
|
48
84
|
let text = '';
|
|
49
85
|
for await (const part of handle.events) {
|
|
50
86
|
if (part.type === 'text-delta') {
|
package/package.json
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
"url": "git+https://github.com/kuralle/kuralle-agents.git",
|
|
7
7
|
"directory": "packages/cli"
|
|
8
8
|
},
|
|
9
|
-
"version": "0.
|
|
9
|
+
"version": "0.15.0",
|
|
10
10
|
"description": "Kuralle CLI — interactive TUI chat, adaptive send, and conversation simulation",
|
|
11
11
|
"type": "module",
|
|
12
12
|
"bin": {
|
|
@@ -20,18 +20,19 @@
|
|
|
20
20
|
"ink-text-input": "^6.0.0",
|
|
21
21
|
"react": "^19.2.7",
|
|
22
22
|
"zod": "^4.0.0",
|
|
23
|
-
"@kuralle-agents/
|
|
24
|
-
"@kuralle-agents/
|
|
23
|
+
"@kuralle-agents/trace-ui": "0.14.1",
|
|
24
|
+
"@kuralle-agents/core": "0.15.0"
|
|
25
25
|
},
|
|
26
26
|
"peerDependencies": {
|
|
27
|
+
"@kuralle-agents/core": "0.x",
|
|
27
28
|
"ai": "^6.0.0",
|
|
28
|
-
"zod": "^4.0.0"
|
|
29
|
-
"@kuralle-agents/core": "0.14.0"
|
|
29
|
+
"zod": "^4.0.0"
|
|
30
30
|
},
|
|
31
31
|
"devDependencies": {
|
|
32
32
|
"@types/node": "^20.11.0",
|
|
33
33
|
"@types/react": "^19.2.17",
|
|
34
|
-
"typescript": "^5.3.0"
|
|
34
|
+
"typescript": "^5.3.0",
|
|
35
|
+
"@kuralle-agents/fs": "0.14.1"
|
|
35
36
|
},
|
|
36
37
|
"publishConfig": {
|
|
37
38
|
"access": "public"
|
|
@@ -45,6 +46,7 @@
|
|
|
45
46
|
"build": "tsc -p tsconfig.json",
|
|
46
47
|
"typecheck": "tsc --noEmit -p tsconfig.json",
|
|
47
48
|
"clean": "rm -rf dist",
|
|
48
|
-
"test": "bun test ./test"
|
|
49
|
+
"test": "bun test ./test",
|
|
50
|
+
"typecheck:examples": "tsc --noEmit -p tsconfig.examples.json"
|
|
49
51
|
}
|
|
50
52
|
}
|