@wipcomputer/wip-ldm-os 0.4.73-alpha.14 → 0.4.73-alpha.16
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/bridge/{chunk-RUQEH7GZ.js → chunk-24DJYS7Z.js} +33 -23
- package/dist/bridge/cli.js +1 -1
- package/dist/bridge/core.d.ts +1 -0
- package/dist/bridge/core.js +1 -1
- package/dist/bridge/mcp-server.js +1 -1
- package/docs/doc-pipeline/README.md +74 -0
- package/docs/doc-pipeline/TECHNICAL.md +79 -0
- package/package.json +1 -1
- package/src/bridge/core.ts +41 -24
|
@@ -9,7 +9,7 @@ var execAsync = promisify(exec);
|
|
|
9
9
|
var GATEWAY_HOST = "127.0.0.1";
|
|
10
10
|
var DEFAULT_GATEWAY_PORT = 18789;
|
|
11
11
|
var DEFAULT_INBOX_PORT = 18790;
|
|
12
|
-
var GATEWAY_TIMEOUT_MS =
|
|
12
|
+
var GATEWAY_TIMEOUT_MS = 12e4;
|
|
13
13
|
var OP_CLI_TIMEOUT_MS = 1e4;
|
|
14
14
|
var EMBEDDING_API_URL = "https://api.openai.com/v1/embeddings";
|
|
15
15
|
var DEFAULT_EMBEDDING_MODEL = "text-embedding-3-small";
|
|
@@ -283,31 +283,41 @@ async function sendMessage(openclawDir, message, options) {
|
|
|
283
283
|
const { token, port } = resolveGatewayConfig(openclawDir);
|
|
284
284
|
const agentId = options?.agentId || "main";
|
|
285
285
|
const senderLabel = options?.senderLabel || "Claude Code";
|
|
286
|
+
const fireAndForget = options?.fireAndForget ?? false;
|
|
287
|
+
const requestBody = JSON.stringify({
|
|
288
|
+
model: `openclaw/${agentId}`,
|
|
289
|
+
messages: [
|
|
290
|
+
{
|
|
291
|
+
role: "user",
|
|
292
|
+
content: `[${senderLabel}]: ${message}`
|
|
293
|
+
}
|
|
294
|
+
]
|
|
295
|
+
});
|
|
296
|
+
const requestHeaders = {
|
|
297
|
+
Authorization: `Bearer ${token}`,
|
|
298
|
+
"Content-Type": "application/json",
|
|
299
|
+
"x-openclaw-scopes": "operator.read,operator.write",
|
|
300
|
+
"x-openclaw-session-key": `agent:${agentId}:main`
|
|
301
|
+
};
|
|
302
|
+
const url = `http://${GATEWAY_HOST}:${port}/v1/chat/completions`;
|
|
303
|
+
if (fireAndForget) {
|
|
304
|
+
fetch(url, {
|
|
305
|
+
method: "POST",
|
|
306
|
+
headers: requestHeaders,
|
|
307
|
+
body: requestBody
|
|
308
|
+
}).catch(() => {
|
|
309
|
+
});
|
|
310
|
+
return "Message sent (queued). Response will arrive in the TUI.";
|
|
311
|
+
}
|
|
286
312
|
const controller = new AbortController();
|
|
287
313
|
const timeoutId = setTimeout(() => controller.abort(), GATEWAY_TIMEOUT_MS);
|
|
288
314
|
try {
|
|
289
|
-
const response = await fetch(
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
"Content-Type": "application/json",
|
|
296
|
-
"x-openclaw-scopes": "operator.read,operator.write",
|
|
297
|
-
"x-openclaw-session-key": `agent:${agentId}:main`
|
|
298
|
-
},
|
|
299
|
-
body: JSON.stringify({
|
|
300
|
-
model: `openclaw/${agentId}`,
|
|
301
|
-
messages: [
|
|
302
|
-
{
|
|
303
|
-
role: "user",
|
|
304
|
-
content: `[${senderLabel}]: ${message}`
|
|
305
|
-
}
|
|
306
|
-
]
|
|
307
|
-
}),
|
|
308
|
-
signal: controller.signal
|
|
309
|
-
}
|
|
310
|
-
);
|
|
315
|
+
const response = await fetch(url, {
|
|
316
|
+
method: "POST",
|
|
317
|
+
headers: requestHeaders,
|
|
318
|
+
body: requestBody,
|
|
319
|
+
signal: controller.signal
|
|
320
|
+
});
|
|
311
321
|
clearTimeout(timeoutId);
|
|
312
322
|
if (!response.ok) {
|
|
313
323
|
const body = await response.text();
|
package/dist/bridge/cli.js
CHANGED
package/dist/bridge/core.d.ts
CHANGED
|
@@ -107,6 +107,7 @@ declare function sendMessage(openclawDir: string, message: string, options?: {
|
|
|
107
107
|
agentId?: string;
|
|
108
108
|
user?: string;
|
|
109
109
|
senderLabel?: string;
|
|
110
|
+
fireAndForget?: boolean;
|
|
110
111
|
}): Promise<string>;
|
|
111
112
|
declare function getQueryEmbedding(text: string, apiKey: string, model?: string, dimensions?: number): Promise<number[]>;
|
|
112
113
|
declare function blobToEmbedding(blob: Buffer): number[];
|
package/dist/bridge/core.js
CHANGED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# Documentation Pipeline
|
|
2
|
+
|
|
3
|
+
Documentation lives in three places. They stay in sync through the installer. This is not optional.
|
|
4
|
+
|
|
5
|
+
## The Three Levels
|
|
6
|
+
|
|
7
|
+
### 1. Repo Docs (source of truth)
|
|
8
|
+
|
|
9
|
+
Every repo has documentation at its root and in `docs/` for features:
|
|
10
|
+
|
|
11
|
+
```
|
|
12
|
+
repo/
|
|
13
|
+
├── README.md What this repo is
|
|
14
|
+
├── TECHNICAL.md How it works
|
|
15
|
+
├── SKILL.md Agent instructions
|
|
16
|
+
├── CLAUDE.md Agent context for Claude Code
|
|
17
|
+
├── docs/
|
|
18
|
+
│ └── <feature>/
|
|
19
|
+
│ ├── README.md What this feature is
|
|
20
|
+
│ └── TECHNICAL.md How this feature works
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
When a feature gets absorbed into a repo, its README and TECHNICAL move into `docs/<feature>/`.
|
|
24
|
+
|
|
25
|
+
Repo docs are the source of truth. Everything else is derived from them.
|
|
26
|
+
|
|
27
|
+
### 2. Home Docs (human readable, personalized)
|
|
28
|
+
|
|
29
|
+
Location: `~/wipcomputerinc/library/documentation/`
|
|
30
|
+
|
|
31
|
+
These are personalized for YOUR system. "Here's how releases work on YOUR machine." Generated by the installer from repo doc templates + your `~/.ldm/config.json`.
|
|
32
|
+
|
|
33
|
+
The human reads these. They describe how the system is set up on this specific machine, with this specific configuration.
|
|
34
|
+
|
|
35
|
+
### 3. Agent Docs (OS reference)
|
|
36
|
+
|
|
37
|
+
Location: `~/.ldm/shared/`
|
|
38
|
+
|
|
39
|
+
```
|
|
40
|
+
~/.ldm/shared/
|
|
41
|
+
├── rules/ Thin rules deployed to ~/.claude/rules/
|
|
42
|
+
├── dev-guide-*.md Org-specific dev conventions
|
|
43
|
+
├── boot/ Boot sequence config
|
|
44
|
+
└── prompts/ Cron prompts
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
These are what agents reference. Rules, dev guide, boot config. The installer deploys them so agents always have current instructions.
|
|
48
|
+
|
|
49
|
+
### 4. ai/ (development process)
|
|
50
|
+
|
|
51
|
+
Location: `<repo>/ai/`
|
|
52
|
+
|
|
53
|
+
Plans, bugs, research, dev updates. Private repo only. Never ships to public. Updated by the dev team (humans + AI agents) during development.
|
|
54
|
+
|
|
55
|
+
## The Update Flow
|
|
56
|
+
|
|
57
|
+
### On merge to private main
|
|
58
|
+
|
|
59
|
+
1. **Repo docs** updated. README, TECHNICAL, docs/<feature>/, SKILL.md, CLAUDE.md. Part of the PR. Code and docs ship together.
|
|
60
|
+
2. **ai/** updated. Plan archived, bugs closed, dev update written. Notes the version is on alpha.
|
|
61
|
+
|
|
62
|
+
### On `ldm install`
|
|
63
|
+
|
|
64
|
+
3. **Home docs** regenerated. Installer reads repo doc templates + config.json, generates personalized `library/documentation/` files.
|
|
65
|
+
4. **Agent docs** deployed. Installer copies rules, dev guide, boot config from the installed package to `~/.ldm/shared/` and `~/.claude/rules/`.
|
|
66
|
+
|
|
67
|
+
### On deploy to public
|
|
68
|
+
|
|
69
|
+
5. **Public repo** updated. `deploy-public.sh` syncs everything except `ai/`.
|
|
70
|
+
6. **ai/** dev update notes the version moved from alpha to release.
|
|
71
|
+
|
|
72
|
+
## The Rule
|
|
73
|
+
|
|
74
|
+
Three places, one update, never out of sync. The installer is the bridge between "code landed" and "docs are current everywhere." Developers write repo docs. The installer propagates them. Nobody manually updates home docs or agent docs.
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
# Documentation Pipeline: Technical Details
|
|
2
|
+
|
|
3
|
+
## File Paths
|
|
4
|
+
|
|
5
|
+
### Repo doc templates (in the LDM OS repo)
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
shared/docs/*.md.tmpl Templates for home docs
|
|
9
|
+
shared/rules/*.md Source for agent rules
|
|
10
|
+
shared/boot/ Boot sequence config
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Templates use placeholders from `~/.ldm/config.json` (workspace path, agent names, org name, etc.).
|
|
14
|
+
|
|
15
|
+
### Installed locations
|
|
16
|
+
|
|
17
|
+
```
|
|
18
|
+
~/.ldm/config.json Org config (agents, paths, co-authors)
|
|
19
|
+
~/.ldm/shared/rules/*.md Agent rules (source for ~/.claude/rules/)
|
|
20
|
+
~/.ldm/shared/dev-guide-*.md Org dev guide
|
|
21
|
+
~/.ldm/shared/boot/ Boot sequence config
|
|
22
|
+
~/.ldm/shared/prompts/ Cron prompts
|
|
23
|
+
~/.ldm/templates/ CLAUDE.md templates, install prompt, etc.
|
|
24
|
+
~/wipcomputerinc/library/documentation/ Personalized human docs (from templates)
|
|
25
|
+
~/.claude/rules/ Deployed rules (copied from ~/.ldm/shared/rules/)
|
|
26
|
+
~/.claude/CLAUDE.md Level 1 global (generated from template + config)
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
### What ldm install deploys
|
|
30
|
+
|
|
31
|
+
| Source | Destination | When |
|
|
32
|
+
|--------|------------|------|
|
|
33
|
+
| `shared/rules/*.md` | `~/.ldm/shared/rules/` then `~/.claude/rules/` | Every install |
|
|
34
|
+
| `shared/docs/*.md.tmpl` | `~/wipcomputerinc/library/documentation/` | Every install |
|
|
35
|
+
| `shared/boot/` | `~/.ldm/shared/boot/` | Every install |
|
|
36
|
+
| `shared/prompts/` | `~/.ldm/shared/prompts/` | Every install |
|
|
37
|
+
| `shared/templates/` | `~/.ldm/templates/` | Every install |
|
|
38
|
+
|
|
39
|
+
**Known bug (April 2026):** The installer currently deploys shared rules on `ldm init` (first install) but not on every `ldm install`. This means rule updates in new versions don't propagate until the user re-initializes. This needs to be fixed so rules deploy on every install.
|
|
40
|
+
|
|
41
|
+
**Known bug (April 2026):** The installer previously deployed home docs to `settings/docs/`. This path was renamed to `library/documentation/` on March 28, 2026. The installer must deploy to the correct path.
|
|
42
|
+
|
|
43
|
+
## How templates work
|
|
44
|
+
|
|
45
|
+
Home doc templates at `shared/docs/*.md.tmpl` contain placeholders:
|
|
46
|
+
|
|
47
|
+
```
|
|
48
|
+
Workspace: {{workspace}}
|
|
49
|
+
Agents: {{agents}}
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
The installer reads `~/.ldm/config.json`, substitutes the placeholders, and writes the personalized docs to `~/wipcomputerinc/library/documentation/`.
|
|
53
|
+
|
|
54
|
+
## CLAUDE.md cascade
|
|
55
|
+
|
|
56
|
+
Three levels. Claude Code reads all of them, walking up from CWD:
|
|
57
|
+
|
|
58
|
+
```
|
|
59
|
+
Level 1: ~/.claude/CLAUDE.md Global. ~30 lines. Universal rules.
|
|
60
|
+
Level 2: ~/wipcomputerinc/CLAUDE.md Workspace. ~150 lines. Org context.
|
|
61
|
+
Level 3: <repo>/CLAUDE.md Per-repo. ~50-86 lines. Repo-specific.
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
After context compaction, CWD may shift to a repo. Without Level 3, all context is lost. Every repo must have a CLAUDE.md.
|
|
65
|
+
|
|
66
|
+
## Config paths (correct as of April 2026)
|
|
67
|
+
|
|
68
|
+
References in CLAUDE.md and rules must use absolute paths:
|
|
69
|
+
|
|
70
|
+
| Reference | Correct path |
|
|
71
|
+
|-----------|-------------|
|
|
72
|
+
| Org config | `~/.ldm/config.json` |
|
|
73
|
+
| Dev guide | `~/.ldm/shared/dev-guide-wipcomputerinc.md` |
|
|
74
|
+
| Human docs | `~/wipcomputerinc/library/documentation/` |
|
|
75
|
+
| Agent rules | `~/.ldm/shared/rules/` (source) or `~/.claude/rules/` (deployed) |
|
|
76
|
+
| Workspace CLAUDE.md | `~/wipcomputerinc/CLAUDE.md` |
|
|
77
|
+
| Global CLAUDE.md | `~/.claude/CLAUDE.md` |
|
|
78
|
+
|
|
79
|
+
NOT `settings/config.json`. NOT `settings/docs/`. Those paths are from before the March 28 rename.
|
package/package.json
CHANGED
package/src/bridge/core.ts
CHANGED
|
@@ -16,7 +16,7 @@ const execAsync = promisify(exec);
|
|
|
16
16
|
const GATEWAY_HOST = "127.0.0.1";
|
|
17
17
|
const DEFAULT_GATEWAY_PORT = 18_789; // openclaw.json gateway.port fallback
|
|
18
18
|
const DEFAULT_INBOX_PORT = 18_790; // env LESA_BRIDGE_INBOX_PORT fallback
|
|
19
|
-
const GATEWAY_TIMEOUT_MS =
|
|
19
|
+
const GATEWAY_TIMEOUT_MS = 120_000; // max wait for gateway chat response (2 min, agent turns can be long)
|
|
20
20
|
const OP_CLI_TIMEOUT_MS = 10_000; // max wait for 1Password CLI
|
|
21
21
|
const EMBEDDING_API_URL = "https://api.openai.com/v1/embeddings";
|
|
22
22
|
const DEFAULT_EMBEDDING_MODEL = "text-embedding-3-small";
|
|
@@ -475,41 +475,58 @@ export function listActiveSessions(agentFilter?: string): SessionInfo[] {
|
|
|
475
475
|
export async function sendMessage(
|
|
476
476
|
openclawDir: string,
|
|
477
477
|
message: string,
|
|
478
|
-
options?: { agentId?: string; user?: string; senderLabel?: string }
|
|
478
|
+
options?: { agentId?: string; user?: string; senderLabel?: string; fireAndForget?: boolean }
|
|
479
479
|
): Promise<string> {
|
|
480
480
|
const { token, port } = resolveGatewayConfig(openclawDir);
|
|
481
481
|
const agentId = options?.agentId || "main";
|
|
482
482
|
const senderLabel = options?.senderLabel || "Claude Code";
|
|
483
|
+
const fireAndForget = options?.fireAndForget ?? false;
|
|
483
484
|
|
|
484
485
|
// Send user: "main" to route to the main session (agent:main:main).
|
|
485
486
|
// This ensures Parker sees CC's messages in the same stream as iMessage.
|
|
486
487
|
// The OpenClaw gateway treats user: "main" as "use the default session."
|
|
488
|
+
const requestBody = JSON.stringify({
|
|
489
|
+
model: `openclaw/${agentId}`,
|
|
490
|
+
messages: [
|
|
491
|
+
{
|
|
492
|
+
role: "user",
|
|
493
|
+
content: `[${senderLabel}]: ${message}`,
|
|
494
|
+
},
|
|
495
|
+
],
|
|
496
|
+
});
|
|
497
|
+
|
|
498
|
+
const requestHeaders: Record<string, string> = {
|
|
499
|
+
Authorization: `Bearer ${token}`,
|
|
500
|
+
"Content-Type": "application/json",
|
|
501
|
+
"x-openclaw-scopes": "operator.read,operator.write",
|
|
502
|
+
"x-openclaw-session-key": `agent:${agentId}:main`,
|
|
503
|
+
};
|
|
504
|
+
|
|
505
|
+
const url = `http://${GATEWAY_HOST}:${port}/v1/chat/completions`;
|
|
506
|
+
|
|
507
|
+
// Fire-and-forget: send the request and return immediately.
|
|
508
|
+
// The message is queued in the gateway. Lēsa processes it when she's ready.
|
|
509
|
+
// No timeout. No waiting. Like dropping a letter in a mailbox.
|
|
510
|
+
if (fireAndForget) {
|
|
511
|
+
fetch(url, {
|
|
512
|
+
method: "POST",
|
|
513
|
+
headers: requestHeaders,
|
|
514
|
+
body: requestBody,
|
|
515
|
+
}).catch(() => {}); // Ignore errors silently
|
|
516
|
+
return "Message sent (queued). Response will arrive in the TUI.";
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
// Synchronous: wait for the full response.
|
|
487
520
|
const controller = new AbortController();
|
|
488
521
|
const timeoutId = setTimeout(() => controller.abort(), GATEWAY_TIMEOUT_MS);
|
|
489
522
|
|
|
490
523
|
try {
|
|
491
|
-
const response = await fetch(
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
"Content-Type": "application/json",
|
|
498
|
-
"x-openclaw-scopes": "operator.read,operator.write",
|
|
499
|
-
"x-openclaw-session-key": `agent:${agentId}:main`,
|
|
500
|
-
},
|
|
501
|
-
body: JSON.stringify({
|
|
502
|
-
model: `openclaw/${agentId}`,
|
|
503
|
-
messages: [
|
|
504
|
-
{
|
|
505
|
-
role: "user",
|
|
506
|
-
content: `[${senderLabel}]: ${message}`,
|
|
507
|
-
},
|
|
508
|
-
],
|
|
509
|
-
}),
|
|
510
|
-
signal: controller.signal,
|
|
511
|
-
}
|
|
512
|
-
);
|
|
524
|
+
const response = await fetch(url, {
|
|
525
|
+
method: "POST",
|
|
526
|
+
headers: requestHeaders,
|
|
527
|
+
body: requestBody,
|
|
528
|
+
signal: controller.signal,
|
|
529
|
+
});
|
|
513
530
|
clearTimeout(timeoutId);
|
|
514
531
|
|
|
515
532
|
if (!response.ok) {
|