scalix 1.5.0 → 1.5.2
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/.env.example +5 -3
- package/CHANGELOG.md +24 -0
- package/LICENSE +0 -1
- package/README.md +47 -2
- package/dist/agent-presets.js +33 -0
- package/dist/agent.js +358 -42
- package/dist/api-client.js +12 -4
- package/dist/board-context.js +81 -0
- package/dist/browser-access.js +0 -0
- package/dist/cli.js +435 -94
- package/dist/cloud.js +95 -6
- package/dist/config.js +44 -5
- package/dist/constants.js +121 -19
- package/dist/context-builder.js +9 -1
- package/dist/feedback.js +51 -0
- package/dist/headless.js +44 -13
- package/dist/ipc-server.js +362 -99
- package/dist/keychain.js +34 -9
- package/dist/mcp.js +470 -28
- package/dist/openrouter-oauth.js +149 -0
- package/dist/project-index.js +46 -23
- package/dist/sandbox.js +115 -27
- package/dist/sessions.js +12 -0
- package/dist/settings.js +33 -2
- package/dist/slash-commands.js +6 -0
- package/dist/sub-agent.js +3 -2
- package/dist/tools-browser.js +87 -16
- package/dist/tools-extra.js +15 -3
- package/dist/tools-memory.js +74 -0
- package/dist/tools-web.js +67 -6
- package/dist/tools.js +15 -14
- package/dist/workspace-trust.js +7 -1
- package/package.json +3 -1
package/dist/api-client.js
CHANGED
|
@@ -28,7 +28,15 @@ export function parseRetryAfter(value) {
|
|
|
28
28
|
return Math.max(0, date - Date.now());
|
|
29
29
|
return undefined;
|
|
30
30
|
}
|
|
31
|
-
|
|
31
|
+
// Per-request abort. 300s matches the platform's own proxy walls; the old
|
|
32
|
+
// 120s default aborted the client before a slow first token could arrive on
|
|
33
|
+
// CPU-served models (the request was fine, we hung up on it). For reference,
|
|
34
|
+
// the OpenAI and Anthropic SDKs both default to 600s.
|
|
35
|
+
const DEFAULT_TIMEOUT_MS = (() => {
|
|
36
|
+
const v = process.env.SCALIX_REQUEST_TIMEOUT_MS;
|
|
37
|
+
const n = v ? parseInt(v, 10) : NaN;
|
|
38
|
+
return Number.isFinite(n) ? n : 0;
|
|
39
|
+
})();
|
|
32
40
|
// The capability probe only decides whether a turn streams. It must never be
|
|
33
41
|
// the reason a turn is slow to start, so it gives up quickly and falls back to
|
|
34
42
|
// the conservative answer.
|
|
@@ -116,8 +124,8 @@ export class ScalixClient {
|
|
|
116
124
|
for (;;) {
|
|
117
125
|
const prepared = this.prepareRequest(req);
|
|
118
126
|
const body = JSON.stringify(prepared);
|
|
119
|
-
const timeoutSignal = AbortSignal.timeout(this.timeoutMs);
|
|
120
|
-
const combined = signal ? AbortSignal.any([signal, timeoutSignal]) : timeoutSignal;
|
|
127
|
+
const timeoutSignal = this.timeoutMs > 0 ? AbortSignal.timeout(this.timeoutMs) : undefined;
|
|
128
|
+
const combined = signal && timeoutSignal ? AbortSignal.any([signal, timeoutSignal]) : (signal || timeoutSignal);
|
|
121
129
|
const response = await fetch(url, {
|
|
122
130
|
method: 'POST',
|
|
123
131
|
headers: {
|
|
@@ -127,7 +135,7 @@ export class ScalixClient {
|
|
|
127
135
|
'x-client-type': 'cli',
|
|
128
136
|
},
|
|
129
137
|
body,
|
|
130
|
-
signal: combined,
|
|
138
|
+
signal: combined || undefined,
|
|
131
139
|
});
|
|
132
140
|
if (!response.ok) {
|
|
133
141
|
const text = await response.text().catch(() => '');
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Crew board context (scalix-org roadmap/SCALIX-CREW.md, T2.2/T2.3).
|
|
3
|
+
*
|
|
4
|
+
* BOARD.md is a READ-ONLY projection of a crew's notice board, written into
|
|
5
|
+
* the workspace by whoever runs the session (the orchestrator's CrewHydrator
|
|
6
|
+
* for cloud sessions; the cockpit for local ones) and refreshed before each
|
|
7
|
+
* turn. The engine never writes it — posting goes through the
|
|
8
|
+
* scalix_crew_board_post platform tool, the board's only write path.
|
|
9
|
+
*
|
|
10
|
+
* Durable lessons do not live here either: a member that learns something
|
|
11
|
+
* permanent from a board note promotes it into project memory (/remember),
|
|
12
|
+
* deliberately. Board = news; memory = knowledge.
|
|
13
|
+
*/
|
|
14
|
+
import fs from 'node:fs/promises';
|
|
15
|
+
import path from 'node:path';
|
|
16
|
+
export const BOARD_FILE = 'BOARD.md';
|
|
17
|
+
/** A board is news, not a document dump — cap what lands in context so a
|
|
18
|
+
* runaway board cannot crowd the task out of the window. */
|
|
19
|
+
export const BOARD_CONTEXT_MAX_CHARS = 16 * 1024;
|
|
20
|
+
/** Read the workspace board projection. Null when absent or empty — those
|
|
21
|
+
* mean "this session has no crew board", which is different from a board
|
|
22
|
+
* with no notes (that renders its own explicit empty state). */
|
|
23
|
+
export async function readBoardFile(cwd) {
|
|
24
|
+
try {
|
|
25
|
+
const raw = await fs.readFile(path.join(cwd, BOARD_FILE), 'utf-8');
|
|
26
|
+
const trimmed = raw.trim();
|
|
27
|
+
if (!trimmed)
|
|
28
|
+
return null;
|
|
29
|
+
return trimmed.length > BOARD_CONTEXT_MAX_CHARS
|
|
30
|
+
? trimmed.slice(0, BOARD_CONTEXT_MAX_CHARS) + '\n…(board truncated for context)'
|
|
31
|
+
: trimmed;
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
export function boardToContext(board) {
|
|
38
|
+
if (!board)
|
|
39
|
+
return '';
|
|
40
|
+
return [
|
|
41
|
+
'## Crew Board (read-only projection)',
|
|
42
|
+
'Shared coordination state from your crew, current as of session start.',
|
|
43
|
+
'Post updates with the scalix_crew_board_post tool. Never edit BOARD.md —',
|
|
44
|
+
'it is overwritten on every refresh and your edit would be silently lost.',
|
|
45
|
+
'',
|
|
46
|
+
board,
|
|
47
|
+
].join('\n');
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Turn-start freshness for a session whose system prompt is assembled once:
|
|
51
|
+
* tracks the last projection seen and, when the file moves, hands back a
|
|
52
|
+
* system note carrying the new copy for the conversation history. One file
|
|
53
|
+
* read per turn; a quiet board costs nothing else.
|
|
54
|
+
*/
|
|
55
|
+
export class BoardTurnRefresher {
|
|
56
|
+
cwd;
|
|
57
|
+
lastSeen = null;
|
|
58
|
+
constructor(cwd) {
|
|
59
|
+
this.cwd = cwd;
|
|
60
|
+
}
|
|
61
|
+
/** Baseline — call once near session start, after the system prompt has
|
|
62
|
+
* embedded the board, so the first turnNote() only fires on real change. */
|
|
63
|
+
async prime() {
|
|
64
|
+
this.lastSeen = await readBoardFile(this.cwd);
|
|
65
|
+
}
|
|
66
|
+
/** Null when nothing changed (or the file vanished — a removed projection
|
|
67
|
+
* is a session-plumbing event, not news to tell the model about). */
|
|
68
|
+
async turnNote() {
|
|
69
|
+
const current = await readBoardFile(this.cwd);
|
|
70
|
+
if (current === this.lastSeen)
|
|
71
|
+
return null;
|
|
72
|
+
this.lastSeen = current;
|
|
73
|
+
if (current === null)
|
|
74
|
+
return null;
|
|
75
|
+
return [
|
|
76
|
+
'## Crew Board (updated since your last turn — supersedes earlier copies)',
|
|
77
|
+
'',
|
|
78
|
+
current,
|
|
79
|
+
].join('\n');
|
|
80
|
+
}
|
|
81
|
+
}
|
|
Binary file
|