castle-web-cli 0.4.178 → 0.4.180
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/agent.js
CHANGED
|
@@ -432,9 +432,13 @@ function handleModelCaps(req, res) {
|
|
|
432
432
|
}
|
|
433
433
|
// Build the headless CLI invocation for a spawning backend/role (smith never
|
|
434
434
|
// comes through here -- it has no CLI process; see runAgentSmith). Cursor's
|
|
435
|
-
// router runs in read-only ask mode; claude runs permission-mode
|
|
436
|
-
// both roles (NOT plan mode -- that makes it emit plan
|
|
437
|
-
// effort.
|
|
435
|
+
// router runs in read-only ask mode; claude runs permission-mode
|
|
436
|
+
// bypassPermissions for both roles (NOT plan mode -- that makes it emit plan
|
|
437
|
+
// tool calls) at medium effort. Not `auto`: auto mode runs a sonnet
|
|
438
|
+
// classifier call beside every turn, on Anthropic's own key, whatever --model
|
|
439
|
+
// says -- a second model's cost and a second billing line per turn. bypass
|
|
440
|
+
// asks no classifier. A cli fix is expected upstream; until then bypass is
|
|
441
|
+
// the mode. claudeModel "openrouter" keeps the SAME claude binary/flags and
|
|
438
442
|
// swaps only the model slug and env (see envForOpenrouterSpawn) -- plus one
|
|
439
443
|
// extra permission grant, see OPENROUTER_ALLOWED_TOOLS below.
|
|
440
444
|
// claude CLI 2.1.198 SILENTLY resolves `--permission-mode auto` to "default"
|
|
@@ -507,11 +511,15 @@ function claudeSettingsArg(auth) {
|
|
|
507
511
|
// new process, so that text (the kit guide: ~23k tokens on a kit deck) is sent
|
|
508
512
|
// uncached on every call of every turn. The router already carries the deck's
|
|
509
513
|
// quick reference and file tree in its cached system part, and can read a
|
|
510
|
-
// guide on demand; tasks keep the CLI's own loading.
|
|
511
|
-
//
|
|
514
|
+
// guide on demand; tasks keep the CLI's own loading. Auto memory is off for the
|
|
515
|
+
// same reason: the first process in a deck creates the memory store, the next
|
|
516
|
+
// one's system prompt gains a memory block, and the cached prefix only matches
|
|
517
|
+
// from the third turn on. Measured on a 30k-token router prompt: a second turn
|
|
518
|
+
// goes from 50k tokens written to 6k.
|
|
512
519
|
const ROUTER_CLAUDE_ENV = {
|
|
513
520
|
CLAUDE_CODE_DISABLE_CLAUDE_MDS: '1',
|
|
514
521
|
CLAUDE_CODE_DISABLE_GIT_INSTRUCTIONS: '1',
|
|
522
|
+
CLAUDE_CODE_DISABLE_AUTO_MEMORY: '1',
|
|
515
523
|
};
|
|
516
524
|
function buildAgentInvocation(backend, role, prompt, claudeModel,
|
|
517
525
|
// Already resolved for this role by the caller (router turns pass
|
|
@@ -549,7 +557,7 @@ systemPrompt) {
|
|
|
549
557
|
'stream-json',
|
|
550
558
|
'--include-partial-messages',
|
|
551
559
|
'--permission-mode',
|
|
552
|
-
'
|
|
560
|
+
'bypassPermissions',
|
|
553
561
|
...(viaOpenrouter ? [openrouterAllowedToolsArg(role)] : []),
|
|
554
562
|
'--model',
|
|
555
563
|
viaOpenrouter ? openrouterModel : claudeModel,
|
|
@@ -2269,6 +2277,8 @@ function persistTaskFile(tasksDir, task) {
|
|
|
2269
2277
|
// written whether or not the deck has the plan doc switched on: it is a disk
|
|
2270
2278
|
// receipt, never injected into a prompt, so it costs an off deck nothing.
|
|
2271
2279
|
const TASK_INDEX_FILE = 'index.md';
|
|
2280
|
+
const TASK_FEED_FILE = 'feed.json';
|
|
2281
|
+
const TASK_FEED_CAP = 80;
|
|
2272
2282
|
const TASK_INDEX_HEADER = '# tasks\n# <task-id> | <item> | <title> | <created> | <status> | <touching>\n';
|
|
2273
2283
|
// A pipe-delimited row can't carry a pipe. Titles come from the router's fence
|
|
2274
2284
|
// and can hold anything.
|
|
@@ -3224,8 +3234,27 @@ function asClientMessage(message, signPath) {
|
|
|
3224
3234
|
attachments: message.attachments.map((name) => clientUrl(`${AGENT_ATTACHMENT_PREFIX}${name}`, signPath)),
|
|
3225
3235
|
};
|
|
3226
3236
|
}
|
|
3227
|
-
function
|
|
3237
|
+
function loadTaskFeeds(tasksDir) {
|
|
3228
3238
|
const map = new Map();
|
|
3239
|
+
for (const entry of fs.existsSync(tasksDir) ? fs.readdirSync(tasksDir) : []) {
|
|
3240
|
+
const loaded = readJsonFile(path.join(tasksDir, entry, TASK_FEED_FILE));
|
|
3241
|
+
if (!Array.isArray(loaded))
|
|
3242
|
+
continue;
|
|
3243
|
+
const lines = loaded.filter((line) => typeof line === 'string');
|
|
3244
|
+
if (lines.length === 0)
|
|
3245
|
+
continue;
|
|
3246
|
+
map.set(entry, lines.slice(-TASK_FEED_CAP));
|
|
3247
|
+
}
|
|
3248
|
+
return map;
|
|
3249
|
+
}
|
|
3250
|
+
function createTaskFeeds(broadcast, tasksDir) {
|
|
3251
|
+
const map = loadTaskFeeds(tasksDir);
|
|
3252
|
+
function persist(task) {
|
|
3253
|
+
const feed = map.get(task.id);
|
|
3254
|
+
if (!feed || feed.length === 0)
|
|
3255
|
+
return;
|
|
3256
|
+
atomicWriteFileSync(path.join(tasksDir, task.id, TASK_FEED_FILE), JSON.stringify(feed, null, 2) + '\n');
|
|
3257
|
+
}
|
|
3229
3258
|
function push(task, entry) {
|
|
3230
3259
|
let feed = map.get(task.id);
|
|
3231
3260
|
if (!feed) {
|
|
@@ -3235,11 +3264,11 @@ function createTaskFeeds(broadcast) {
|
|
|
3235
3264
|
if (feed[feed.length - 1] === entry)
|
|
3236
3265
|
return;
|
|
3237
3266
|
feed.push(entry);
|
|
3238
|
-
if (feed.length >
|
|
3239
|
-
feed.splice(0, feed.length -
|
|
3267
|
+
if (feed.length > TASK_FEED_CAP)
|
|
3268
|
+
feed.splice(0, feed.length - TASK_FEED_CAP);
|
|
3240
3269
|
broadcast({ type: 'task-feed', id: task.id, entry });
|
|
3241
3270
|
}
|
|
3242
|
-
return { map, push };
|
|
3271
|
+
return { map, push, persist };
|
|
3243
3272
|
}
|
|
3244
3273
|
function createMessageThinking(broadcast) {
|
|
3245
3274
|
const map = new Map();
|
|
@@ -4399,7 +4428,7 @@ export function createAgentServer(opts) {
|
|
|
4399
4428
|
// Castle's), so the bar follows the change instead of the poll.
|
|
4400
4429
|
usageFeed.refresh();
|
|
4401
4430
|
};
|
|
4402
|
-
const taskFeeds = createTaskFeeds(broadcast);
|
|
4431
|
+
const taskFeeds = createTaskFeeds(broadcast, tasksDir);
|
|
4403
4432
|
const messageThinking = createMessageThinking(broadcast);
|
|
4404
4433
|
const taskStore = createTaskStore({
|
|
4405
4434
|
deckDir,
|
|
@@ -4421,7 +4450,7 @@ export function createAgentServer(opts) {
|
|
|
4421
4450
|
onUpdate: (task) => broadcast({ type: 'task-update', task: asClientTask(task, opts.signPath) }),
|
|
4422
4451
|
onStarted: () => undefined,
|
|
4423
4452
|
onRetry: (task, attempt) => addLog(`agent died, retrying (${attempt}/${MAX_TASK_ATTEMPTS}): ${task.title}`),
|
|
4424
|
-
onFinished: (task) => taskFeeds.
|
|
4453
|
+
onFinished: (task) => taskFeeds.persist(task),
|
|
4425
4454
|
onFeed: (task, entry) => taskFeeds.push(task, entry),
|
|
4426
4455
|
});
|
|
4427
4456
|
// The mid-run send queue (queue-by-default, "send now"/Stop interrupt, epoch
|