wayang-ai 0.8.2 → 0.8.3
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/cli.js +242 -180
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// src/cli.ts
|
|
4
|
-
import * as
|
|
4
|
+
import * as path9 from "path";
|
|
5
5
|
|
|
6
6
|
// src/cli-args.ts
|
|
7
7
|
import meow from "meow";
|
|
@@ -463,11 +463,11 @@ var RemoteStateStore = class {
|
|
|
463
463
|
rpc;
|
|
464
464
|
entries = /* @__PURE__ */ new Map();
|
|
465
465
|
byId = /* @__PURE__ */ new Map();
|
|
466
|
-
subscribe(target,
|
|
467
|
-
const key = `${target}:${
|
|
466
|
+
subscribe(target, path10, callback) {
|
|
467
|
+
const key = `${target}:${path10}`;
|
|
468
468
|
let entry = this.entries.get(key);
|
|
469
469
|
if (!entry) {
|
|
470
|
-
entry = { subscriptionId: "", target, path:
|
|
470
|
+
entry = { subscriptionId: "", target, path: path10, data: void 0, listeners: /* @__PURE__ */ new Set(), settled: false };
|
|
471
471
|
this.entries.set(key, entry);
|
|
472
472
|
this.connect(entry, key);
|
|
473
473
|
}
|
|
@@ -477,8 +477,8 @@ var RemoteStateStore = class {
|
|
|
477
477
|
if (entry.listeners.size === 0) this.teardown(entry, key);
|
|
478
478
|
};
|
|
479
479
|
}
|
|
480
|
-
getSnapshot(target,
|
|
481
|
-
return this.entries.get(`${target}:${
|
|
480
|
+
getSnapshot(target, path10) {
|
|
481
|
+
return this.entries.get(`${target}:${path10}`)?.data;
|
|
482
482
|
}
|
|
483
483
|
connect(entry, key) {
|
|
484
484
|
this.rpc.request(METHOD.SUBSCRIBE, { target: entry.target, path: entry.path }).then((res) => {
|
|
@@ -516,11 +516,11 @@ var RemoteSubscribable = class {
|
|
|
516
516
|
}
|
|
517
517
|
store;
|
|
518
518
|
target;
|
|
519
|
-
subscribe(
|
|
520
|
-
return this.store.subscribe(this.target,
|
|
519
|
+
subscribe(path10, callback) {
|
|
520
|
+
return this.store.subscribe(this.target, path10, callback);
|
|
521
521
|
}
|
|
522
|
-
getSnapshot(
|
|
523
|
-
return this.store.getSnapshot(this.target,
|
|
522
|
+
getSnapshot(path10) {
|
|
523
|
+
return this.store.getSnapshot(this.target, path10);
|
|
524
524
|
}
|
|
525
525
|
};
|
|
526
526
|
|
|
@@ -1475,43 +1475,43 @@ var BaseWayangState = class {
|
|
|
1475
1475
|
}
|
|
1476
1476
|
// --- Read ---
|
|
1477
1477
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- public default generic: bare get() callers across the repo rely on it; per-call sites pass an explicit T
|
|
1478
|
-
get(
|
|
1479
|
-
return get(this.data,
|
|
1478
|
+
get(path10) {
|
|
1479
|
+
return get(this.data, path10);
|
|
1480
1480
|
}
|
|
1481
1481
|
// --- Write operations ---
|
|
1482
|
-
set(
|
|
1483
|
-
const prev = get(this.data,
|
|
1484
|
-
set(this.data,
|
|
1485
|
-
this.persist(
|
|
1486
|
-
this.notify(
|
|
1482
|
+
set(path10, value) {
|
|
1483
|
+
const prev = get(this.data, path10);
|
|
1484
|
+
set(this.data, path10, value);
|
|
1485
|
+
this.persist(path10, "set");
|
|
1486
|
+
this.notify(path10, "set", prev);
|
|
1487
1487
|
}
|
|
1488
1488
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- partial update merges an untyped object into the state tree root (see `data`)
|
|
1489
|
-
update(
|
|
1490
|
-
const prev = cloneDeep(get(this.data,
|
|
1491
|
-
const target = get(this.data,
|
|
1489
|
+
update(path10, partial) {
|
|
1490
|
+
const prev = cloneDeep(get(this.data, path10));
|
|
1491
|
+
const target = get(this.data, path10);
|
|
1492
1492
|
if (target && typeof target === "object") {
|
|
1493
1493
|
Object.assign(target, partial);
|
|
1494
1494
|
} else {
|
|
1495
|
-
set(this.data,
|
|
1495
|
+
set(this.data, path10, partial);
|
|
1496
1496
|
}
|
|
1497
|
-
this.persist(
|
|
1498
|
-
this.notify(
|
|
1497
|
+
this.persist(path10, "update");
|
|
1498
|
+
this.notify(path10, "update", prev);
|
|
1499
1499
|
}
|
|
1500
|
-
append(
|
|
1501
|
-
const arr = get(this.data,
|
|
1500
|
+
append(path10, entry) {
|
|
1501
|
+
const arr = get(this.data, path10);
|
|
1502
1502
|
if (!Array.isArray(arr)) {
|
|
1503
|
-
throw new Error(`State.append: path "${
|
|
1503
|
+
throw new Error(`State.append: path "${path10}" is not an array`);
|
|
1504
1504
|
}
|
|
1505
|
-
const stamped =
|
|
1505
|
+
const stamped = path10 === "conversation" ? applyAppendEnvelope(entry, arr) : entry;
|
|
1506
1506
|
arr.push(stamped);
|
|
1507
|
-
set(this.data,
|
|
1508
|
-
this.persist(
|
|
1509
|
-
this.notify(
|
|
1507
|
+
set(this.data, path10, [...arr]);
|
|
1508
|
+
this.persist(path10, "append", stamped);
|
|
1509
|
+
this.notify(path10, "append", void 0);
|
|
1510
1510
|
}
|
|
1511
|
-
remove(
|
|
1512
|
-
const arr = get(this.data,
|
|
1511
|
+
remove(path10, idx) {
|
|
1512
|
+
const arr = get(this.data, path10);
|
|
1513
1513
|
if (!Array.isArray(arr)) {
|
|
1514
|
-
throw new Error(`State.remove: path "${
|
|
1514
|
+
throw new Error(`State.remove: path "${path10}" is not an array`);
|
|
1515
1515
|
}
|
|
1516
1516
|
const prev = idx !== void 0 ? arr[idx] : arr[arr.length - 1];
|
|
1517
1517
|
if (idx === void 0) {
|
|
@@ -1519,16 +1519,16 @@ var BaseWayangState = class {
|
|
|
1519
1519
|
} else {
|
|
1520
1520
|
arr.splice(idx, 1);
|
|
1521
1521
|
}
|
|
1522
|
-
this.persist(
|
|
1523
|
-
this.notify(
|
|
1522
|
+
this.persist(path10, "remove");
|
|
1523
|
+
this.notify(path10, "remove", prev);
|
|
1524
1524
|
}
|
|
1525
1525
|
// --- Subscription ---
|
|
1526
|
-
on(
|
|
1527
|
-
if (!this.subscriptions.has(
|
|
1528
|
-
this.subscriptions.set(
|
|
1526
|
+
on(path10, callback) {
|
|
1527
|
+
if (!this.subscriptions.has(path10)) {
|
|
1528
|
+
this.subscriptions.set(path10, /* @__PURE__ */ new Set());
|
|
1529
1529
|
}
|
|
1530
|
-
this.subscriptions.get(
|
|
1531
|
-
return () => this.subscriptions.get(
|
|
1530
|
+
this.subscriptions.get(path10).add(callback);
|
|
1531
|
+
return () => this.subscriptions.get(path10)?.delete(callback);
|
|
1532
1532
|
}
|
|
1533
1533
|
// --- Persistence ---
|
|
1534
1534
|
/**
|
|
@@ -1536,8 +1536,8 @@ var BaseWayangState = class {
|
|
|
1536
1536
|
* for the helper to pick its own strategy. Snapshot helpers replace the
|
|
1537
1537
|
* file; append helpers write only the new entry and ignore window edits.
|
|
1538
1538
|
*/
|
|
1539
|
-
persist(
|
|
1540
|
-
const topKey =
|
|
1539
|
+
persist(path10, op, entry) {
|
|
1540
|
+
const topKey = path10.split(".")[0];
|
|
1541
1541
|
for (const mapping of this.persistMappings) {
|
|
1542
1542
|
if (mapping.path !== topKey) continue;
|
|
1543
1543
|
mapping.helper.write({
|
|
@@ -2169,8 +2169,8 @@ function isSameWorkspacePath(a, b) {
|
|
|
2169
2169
|
function resolveWorkspaceSlug(workspace) {
|
|
2170
2170
|
const slug = workspace?.slug?.trim();
|
|
2171
2171
|
if (slug) return slug;
|
|
2172
|
-
const
|
|
2173
|
-
return
|
|
2172
|
+
const path10 = workspace?.path?.trim();
|
|
2173
|
+
return path10 ? workspaceSlugFromPath(path10) : null;
|
|
2174
2174
|
}
|
|
2175
2175
|
function formatTaskWorkspaceMark(workspace, defaultWorkspacePath) {
|
|
2176
2176
|
const slug = resolveWorkspaceSlug(workspace);
|
|
@@ -2364,27 +2364,27 @@ function toolTarget(toolName, args, budget) {
|
|
|
2364
2364
|
return fitWithSuffix(lines[0].trim(), note, budget);
|
|
2365
2365
|
}
|
|
2366
2366
|
case "write_file": {
|
|
2367
|
-
const
|
|
2368
|
-
if (!
|
|
2367
|
+
const path10 = argString(args, ["path", "file_path"]);
|
|
2368
|
+
if (!path10) return "";
|
|
2369
2369
|
const lines = lineCount(args.content);
|
|
2370
2370
|
const note = lines > 0 ? ` (+${lines})` : "";
|
|
2371
|
-
return fitWithSuffix(shortenPath(
|
|
2371
|
+
return fitWithSuffix(shortenPath(path10), note, budget);
|
|
2372
2372
|
}
|
|
2373
2373
|
case "edit_file": {
|
|
2374
|
-
const
|
|
2375
|
-
if (!
|
|
2374
|
+
const path10 = argString(args, ["path", "file_path"]);
|
|
2375
|
+
if (!path10) return "";
|
|
2376
2376
|
const oldLines = lineCount(args.old_string);
|
|
2377
2377
|
const newLines = lineCount(args.new_string);
|
|
2378
2378
|
const note = oldLines > 0 || newLines > 0 ? ` (${oldLines}\u2192${newLines} \u884C)` : "";
|
|
2379
|
-
return fitWithSuffix(shortenPath(
|
|
2379
|
+
return fitWithSuffix(shortenPath(path10), note, budget);
|
|
2380
2380
|
}
|
|
2381
2381
|
case "read_file": {
|
|
2382
|
-
const
|
|
2383
|
-
if (!
|
|
2382
|
+
const path10 = argString(args, ["path", "file_path"]);
|
|
2383
|
+
if (!path10) return "";
|
|
2384
2384
|
const offset = argNumber(args, ["offset"]);
|
|
2385
2385
|
const limit = argNumber(args, ["limit"]);
|
|
2386
2386
|
const range = offset !== void 0 ? `:${offset}${limit !== void 0 ? `+${limit}` : ""}` : "";
|
|
2387
|
-
return truncateToWidth(shortenPath(
|
|
2387
|
+
return truncateToWidth(shortenPath(path10) + range, budget);
|
|
2388
2388
|
}
|
|
2389
2389
|
case "search_files":
|
|
2390
2390
|
return truncateToWidth(argString(args, ["pattern"]) ?? "", budget);
|
|
@@ -4063,15 +4063,15 @@ import { Box as Box8, Text as Text9, useStdout } from "ink";
|
|
|
4063
4063
|
|
|
4064
4064
|
// src/tui/hooks/use-wayang-state.ts
|
|
4065
4065
|
import { useState as useState4, useEffect as useEffect3 } from "react";
|
|
4066
|
-
function useWayangState(source,
|
|
4067
|
-
const [value, setValue] = useState4(() => source.getSnapshot(
|
|
4066
|
+
function useWayangState(source, path10) {
|
|
4067
|
+
const [value, setValue] = useState4(() => source.getSnapshot(path10));
|
|
4068
4068
|
useEffect3(() => {
|
|
4069
|
-
const unSub = source.subscribe(
|
|
4070
|
-
setValue(source.getSnapshot(
|
|
4069
|
+
const unSub = source.subscribe(path10, () => {
|
|
4070
|
+
setValue(source.getSnapshot(path10));
|
|
4071
4071
|
});
|
|
4072
|
-
setValue(source.getSnapshot(
|
|
4072
|
+
setValue(source.getSnapshot(path10));
|
|
4073
4073
|
return unSub;
|
|
4074
|
-
}, [source,
|
|
4074
|
+
}, [source, path10]);
|
|
4075
4075
|
return value;
|
|
4076
4076
|
}
|
|
4077
4077
|
|
|
@@ -4571,7 +4571,7 @@ function AppContent() {
|
|
|
4571
4571
|
// package.json
|
|
4572
4572
|
var package_default = {
|
|
4573
4573
|
name: "wayang-ai",
|
|
4574
|
-
version: "0.8.
|
|
4574
|
+
version: "0.8.3",
|
|
4575
4575
|
description: "Multi-agent orchestration platform",
|
|
4576
4576
|
type: "module",
|
|
4577
4577
|
license: "MIT",
|
|
@@ -5471,11 +5471,11 @@ var SignalQueue = class {
|
|
|
5471
5471
|
this.counter = Math.max(mark, this.maxIdInWindow());
|
|
5472
5472
|
}
|
|
5473
5473
|
// --- Subscribable ---
|
|
5474
|
-
subscribe(
|
|
5475
|
-
return this.state.on(
|
|
5474
|
+
subscribe(path10, callback) {
|
|
5475
|
+
return this.state.on(path10, callback);
|
|
5476
5476
|
}
|
|
5477
|
-
getSnapshot(
|
|
5478
|
-
return this.state.get(
|
|
5477
|
+
getSnapshot(path10) {
|
|
5478
|
+
return this.state.get(path10);
|
|
5479
5479
|
}
|
|
5480
5480
|
enqueue(sig) {
|
|
5481
5481
|
const id = `sig-${++this.counter}`;
|
|
@@ -6104,9 +6104,9 @@ function workerTranscriptPath(sessionDir, workerId) {
|
|
|
6104
6104
|
}
|
|
6105
6105
|
function readWorkerTranscript(sessionDir, workerId) {
|
|
6106
6106
|
if (!isValidWorkerId(workerId)) return null;
|
|
6107
|
-
const
|
|
6108
|
-
if (!existsSync8(
|
|
6109
|
-
return new JSONLFileHelper(
|
|
6107
|
+
const path10 = workerTranscriptPath(sessionDir, workerId);
|
|
6108
|
+
if (!existsSync8(path10)) return null;
|
|
6109
|
+
return new JSONLFileHelper(path10).read();
|
|
6110
6110
|
}
|
|
6111
6111
|
var MAX_TRANSCRIPT_WINDOW_BYTES = 2 * 1024 * 1024;
|
|
6112
6112
|
var MIN_WINDOW_BYTES = 64 * 1024;
|
|
@@ -6136,10 +6136,10 @@ function parseWindow(text, startsAtFileHead) {
|
|
|
6136
6136
|
}
|
|
6137
6137
|
function readWorkerTranscriptWindow(sessionDir, workerId, opts = {}) {
|
|
6138
6138
|
if (!isValidWorkerId(workerId)) return null;
|
|
6139
|
-
const
|
|
6139
|
+
const path10 = workerTranscriptPath(sessionDir, workerId);
|
|
6140
6140
|
let size;
|
|
6141
6141
|
try {
|
|
6142
|
-
size = statSync2(
|
|
6142
|
+
size = statSync2(path10).size;
|
|
6143
6143
|
} catch {
|
|
6144
6144
|
return null;
|
|
6145
6145
|
}
|
|
@@ -6153,7 +6153,7 @@ function readWorkerTranscriptWindow(sessionDir, workerId, opts = {}) {
|
|
|
6153
6153
|
const readLen = Math.min(size, windowBytes);
|
|
6154
6154
|
const start = size - readLen;
|
|
6155
6155
|
const chunk = Buffer.alloc(readLen);
|
|
6156
|
-
const fd = openSync4(
|
|
6156
|
+
const fd = openSync4(path10, "r");
|
|
6157
6157
|
try {
|
|
6158
6158
|
readSync2(fd, chunk, 0, readLen, start);
|
|
6159
6159
|
} finally {
|
|
@@ -7057,6 +7057,23 @@ ${lines}`
|
|
|
7057
7057
|
);
|
|
7058
7058
|
}
|
|
7059
7059
|
|
|
7060
|
+
// src/infra/general-workspace.ts
|
|
7061
|
+
import { mkdirSync as mkdirSync6 } from "fs";
|
|
7062
|
+
import { homedir } from "os";
|
|
7063
|
+
import { join as join14 } from "path";
|
|
7064
|
+
var GENERAL_WORKSPACE_DIRNAME = "workspace";
|
|
7065
|
+
function wayangDirFor(ctx) {
|
|
7066
|
+
return wayangDirForSessionDir(ctx.sessionDir) ?? join14(homedir(), ".wayang");
|
|
7067
|
+
}
|
|
7068
|
+
function resolveGeneralWorkspace(ctx) {
|
|
7069
|
+
return join14(wayangDirFor(ctx), GENERAL_WORKSPACE_DIRNAME);
|
|
7070
|
+
}
|
|
7071
|
+
function ensureGeneralWorkspace(ctx) {
|
|
7072
|
+
const dir = resolveGeneralWorkspace(ctx);
|
|
7073
|
+
mkdirSync6(dir, { recursive: true });
|
|
7074
|
+
return dir;
|
|
7075
|
+
}
|
|
7076
|
+
|
|
7060
7077
|
// src/services/agents/prompts/controller-prompt.ts
|
|
7061
7078
|
var IDENTITY = section(
|
|
7062
7079
|
"Identity",
|
|
@@ -7333,13 +7350,17 @@ You resumed a previous session that had unfinished tasks. Workers were NOT autom
|
|
|
7333
7350
|
|
|
7334
7351
|
Do NOT blindly re-create everything. The user may have moved on. Prefer to summarize and ask unless the intent is obvious.`
|
|
7335
7352
|
);
|
|
7336
|
-
|
|
7337
|
-
|
|
7338
|
-
`\u3010\u4EFB\u52A1\u62C6\u5206\u3011
|
|
7353
|
+
function buildWorkingPrinciples(generalWorkspace) {
|
|
7354
|
+
const body = `\u3010\u4EFB\u52A1\u62C6\u5206\u3011
|
|
7339
7355
|
- \u4E00\u4E2A\u4EFB\u52A1\u53EA\u4EA4\u4ED8\u4E00\u4E2A\u53EF\u72EC\u7ACB\u9A8C\u6536\u7684\u7ED3\u679C\uFF1B\u4E0D\u8981\u628A\u8BBE\u8BA1\u3001\u5B9E\u73B0\u3001\u6D4B\u8BD5\u585E\u8FDB\u540C\u4E00\u6BB5\u3002
|
|
7340
7356
|
- \u5148\u505A\u80FD\u88AB\u5355\u6D4B\u65AD\u8A00\u7684\u6838\u5FC3\u903B\u8F91\uFF0C\u628A\u8FB9\u754C\uFF08\u5BBD\u5B57\u7B26\u3001\u4EE3\u7406\u5BF9\u3001\u6298\u884C\u4F4D\u7F6E\u4E4B\u7C7B\uFF09\u56FA\u5316\u6210\u7528\u4F8B\uFF0C\u518D\u53E0\u52A0\u96C6\u6210\u4E0E\u4EA4\u4E92\u5C42\u3002
|
|
7341
7357
|
- \u4EFB\u52A1\u63CF\u8FF0\u5FC5\u987B\u81EA\u5305\u542B\uFF1A\u80CC\u666F\u3001\u76EE\u6807\u3001\u8F93\u5165\u8F93\u51FA\u3001\u7EA6\u675F\u3001\u9A8C\u6536\u6807\u51C6\u3002\u6267\u884C\u65B9\u770B\u4E0D\u5230\u4F60\u7684\u5BF9\u8BDD\u3002
|
|
7342
7358
|
|
|
7359
|
+
\u3010\u5DE5\u4F5C\u533A\u3011
|
|
7360
|
+
- \u7528\u6237\u7ED9\u4E86\u76EE\u5F55\uFF0C\u6216\u4EFB\u52A1\u660E\u663E\u5C5E\u4E8E\u67D0\u4E2A\u4EE3\u7801\u5E93\uFF1A\u7528\u90A3\u4E2A\u76EE\u5F55\u3002
|
|
7361
|
+
- \u7528\u6237\u6CA1\u6307\u5B9A\u3001\u6216\u4EFB\u52A1\u4E0E\u4EE3\u7801\u5E93\u65E0\u5173\uFF08\u8C03\u7814\u3001\u8D44\u6599\u6574\u7406\u3001\u4FE1\u606F\u67E5\u8BE2\u3001\u5199\u6587\u6863\u8349\u7A3F\uFF09\uFF1A\u7528\u901A\u7528\u5DE5\u4F5C\u533A ${generalWorkspace}\uFF0C\u5B83\u4E0D\u5C5E\u4E8E\u4EFB\u4F55\u4ED3\u5E93\uFF0C\u9996\u6B21\u4F7F\u7528\u81EA\u52A8\u521B\u5EFA\u3002
|
|
7362
|
+
- \u4EFB\u52A1\u4F1A\u7559\u4E0B\u4E2D\u95F4\u4EA7\u7269\u65F6\u5199\u8FDB\u63CF\u8FF0\uFF1A\u4E34\u65F6\u6587\u4EF6\u653E\u4EFB\u52A1 workspace \u5185\u7684 .agents/tmp/\uFF0C\u4E0D\u8981\u7528 /tmp\u3002
|
|
7363
|
+
|
|
7343
7364
|
\u3010\u5206\u5DE5\u4E0E\u5BA1\u6838\u3011
|
|
7344
7365
|
- \u8BBE\u8BA1\u5224\u65AD\u4E0E\u673A\u68B0\u5B9E\u73B0\u5206\u5F00\u6D3E\uFF1A\u8BBE\u8BA1\u65B9\u5148\u5B9E\u6D4B\u5173\u952E\u884C\u4E3A\uFF0C\u4EA7\u51FA\u300C\u6539\u54EA\u4E2A\u6587\u4EF6\u3001\u51FD\u6570\u7B7E\u540D\u3001\u6570\u636E\u7ED3\u6784\u3001\u8FB9\u754C\u60C5\u51B5\u3001\u6D4B\u8BD5\u6E05\u5355\u300D\u7684\u65B9\u6848\uFF1B\u6267\u884C\u65B9\u7167\u65B9\u6848\u7F16\u7801\uFF0C\u4E0D\u505A\u8BBE\u8BA1\u51B3\u7B56\u3002
|
|
7345
7366
|
- \u65B9\u6848\u91CC\u8981\u663E\u5F0F\u5199\u51FA\u300C\u4E0D\u8981\u505A\u4EC0\u4E48\u300D\uFF0C\u5426\u5219\u6267\u884C\u65B9\u4F1A\u987A\u624B\u91CD\u6784\u65E0\u5173\u4EE3\u7801\u3002
|
|
@@ -7364,8 +7385,9 @@ var WORKING_PRINCIPLES = section(
|
|
|
7364
7385
|
- \u540C\u533A\u4E92\u5377 \u2014\u2014 \u8BA9\u4E24\u4E2A\u6267\u884C\u8005\u5728\u540C\u4E00\u5DE5\u4F5C\u533A\u5E76\u53D1\u63D0\u4EA4\uFF0C\u4E92\u76F8\u628A\u5BF9\u65B9\u7684\u6539\u52A8\u5377\u8FDB\u81EA\u5DF1\u7684 commit\u3002
|
|
7365
7386
|
- \u9759\u9ED8\u5224\u6B7B \u2014\u2014 \u51ED\u8F93\u51FA\u9759\u9ED8\u5224\u65AD\u6B7B\u6D3B\uFF1A\u628A\u5728\u8DD1\u957F\u4EFB\u52A1\u5F53\u6210\u5361\u6B7B\u6740\u6389\uFF0C\u6216\u628A\u6D3B\u9501\u5F53\u6210\u6162\u4EFB\u52A1\u6B7B\u7B49\u3002
|
|
7366
7387
|
- \u7EFF\u706F\u5373\u5B8C\u6210 \u2014\u2014 \u5355\u6D4B\u6216\u6D41\u7A0B\u5168\u7EFF\u5C31\u5BA3\u5E03\u5B8C\u6210\uFF1B\u5076\u53D1\u5931\u8D25\u590D\u8DD1\u4E00\u6B21\u901A\u8FC7\u5C31\u5F53\u6CA1\u53D1\u751F\uFF0C\u4E0D\u505A\u8BB0\u5F55\u3002
|
|
7367
|
-
- \u96F6\u788E\u53D6\u8BC1 \u2014\u2014 \u628A\u4E00\u6B21\u53D6\u8BC1\u62C6\u6210\u51E0\u5341\u6761\u96F6\u788E\u53EA\u8BFB\u547D\u4EE4\u9010\u6761\u7B49\u6279\u51C6\uFF0C\u6392\u969C\u88AB\u6D41\u7A0B\u672C\u8EAB\u62D6\u6B7B\u3002
|
|
7368
|
-
);
|
|
7388
|
+
- \u96F6\u788E\u53D6\u8BC1 \u2014\u2014 \u628A\u4E00\u6B21\u53D6\u8BC1\u62C6\u6210\u51E0\u5341\u6761\u96F6\u788E\u53EA\u8BFB\u547D\u4EE4\u9010\u6761\u7B49\u6279\u51C6\uFF0C\u6392\u969C\u88AB\u6D41\u7A0B\u672C\u8EAB\u62D6\u6B7B\u3002`;
|
|
7389
|
+
return section("Working principles", body);
|
|
7390
|
+
}
|
|
7369
7391
|
var HARD_CONSTRAINTS = section(
|
|
7370
7392
|
"Hard constraints",
|
|
7371
7393
|
`- NEVER execute commands or write files yourself. Delegate via add_task\uFF08worker will execute\uFF09.
|
|
@@ -7391,7 +7413,7 @@ function buildControllerSystemPrompt(ctx, skills, opts) {
|
|
|
7391
7413
|
TOOL_USAGE,
|
|
7392
7414
|
PLANNING_TOOLS,
|
|
7393
7415
|
SIGNAL_HANDLING,
|
|
7394
|
-
|
|
7416
|
+
buildWorkingPrinciples(resolveGeneralWorkspace(ctx)),
|
|
7395
7417
|
HARD_CONSTRAINTS,
|
|
7396
7418
|
buildWorkerList(ctx.config.workers),
|
|
7397
7419
|
buildSkillCatalog(skills),
|
|
@@ -7404,7 +7426,7 @@ function buildControllerSystemPrompt(ctx, skills, opts) {
|
|
|
7404
7426
|
|
|
7405
7427
|
// src/services/agents/prompts/controller-rules.ts
|
|
7406
7428
|
import { readFileSync as readFileSync8, realpathSync as realpathSync2, statSync as statSync3 } from "fs";
|
|
7407
|
-
import { join as
|
|
7429
|
+
import { join as join15 } from "path";
|
|
7408
7430
|
var CONTROLLER_RULES_FILENAME = "AGENTS.md";
|
|
7409
7431
|
var CONTROLLER_RULES_MAX_BYTES = 32 * 1024;
|
|
7410
7432
|
function loadControllerRules(opts) {
|
|
@@ -7451,13 +7473,13 @@ ${intro}`, ...bodies];
|
|
|
7451
7473
|
return parts.join("\n\n");
|
|
7452
7474
|
}
|
|
7453
7475
|
function readRulesFile(dir) {
|
|
7454
|
-
const candidate =
|
|
7476
|
+
const candidate = join15(dir, CONTROLLER_RULES_FILENAME);
|
|
7455
7477
|
try {
|
|
7456
|
-
const
|
|
7457
|
-
if (!statSync3(
|
|
7458
|
-
const content = readFileSync8(
|
|
7478
|
+
const path10 = realpathSync2(candidate);
|
|
7479
|
+
if (!statSync3(path10).isFile()) return null;
|
|
7480
|
+
const content = readFileSync8(path10, "utf8");
|
|
7459
7481
|
if (!content.trim()) return null;
|
|
7460
|
-
return { path:
|
|
7482
|
+
return { path: path10, content };
|
|
7461
7483
|
} catch {
|
|
7462
7484
|
return null;
|
|
7463
7485
|
}
|
|
@@ -7470,7 +7492,7 @@ function truncateToBytes(text, maxBytes) {
|
|
|
7470
7492
|
|
|
7471
7493
|
// src/services/agents/prompts/project-rules.ts
|
|
7472
7494
|
import { existsSync as existsSync9, readFileSync as readFileSync9, statSync as statSync4 } from "fs";
|
|
7473
|
-
import { basename as basename5, dirname as dirname6, join as
|
|
7495
|
+
import { basename as basename5, dirname as dirname6, join as join16, resolve as resolve3 } from "path";
|
|
7474
7496
|
var RULE_FILE_NAMES = ["AGENTS.md", "CLAUDE.md"];
|
|
7475
7497
|
var MAX_LEVELS = 5;
|
|
7476
7498
|
var PROJECT_RULES_MAX_LENGTH = 8e3;
|
|
@@ -7479,7 +7501,7 @@ function findProjectRules(startDir) {
|
|
|
7479
7501
|
for (let level = 0; level < MAX_LEVELS; level++) {
|
|
7480
7502
|
const found = readRuleFile(current);
|
|
7481
7503
|
if (found) return found;
|
|
7482
|
-
if (existsSync9(
|
|
7504
|
+
if (existsSync9(join16(current, ".git"))) break;
|
|
7483
7505
|
const parent = dirname6(current);
|
|
7484
7506
|
if (parent === current) break;
|
|
7485
7507
|
current = parent;
|
|
@@ -7488,12 +7510,12 @@ function findProjectRules(startDir) {
|
|
|
7488
7510
|
}
|
|
7489
7511
|
function readRuleFile(dir) {
|
|
7490
7512
|
for (const name of RULE_FILE_NAMES) {
|
|
7491
|
-
const
|
|
7513
|
+
const path10 = join16(dir, name);
|
|
7492
7514
|
try {
|
|
7493
|
-
if (!statSync4(
|
|
7494
|
-
const content = readFileSync9(
|
|
7515
|
+
if (!statSync4(path10).isFile()) continue;
|
|
7516
|
+
const content = readFileSync9(path10, "utf8");
|
|
7495
7517
|
if (!content.trim()) continue;
|
|
7496
|
-
return { path:
|
|
7518
|
+
return { path: path10, content };
|
|
7497
7519
|
} catch {
|
|
7498
7520
|
}
|
|
7499
7521
|
}
|
|
@@ -7569,6 +7591,9 @@ Only use bash when no dedicated tool fits the task (e.g. running builds, install
|
|
|
7569
7591
|
- edit_file modifies an existing file by replacing a unique string. The old_string must appear exactly once. If it matches multiple times, the tool will tell you \u2014 expand the surrounding context to make it unique.
|
|
7570
7592
|
- Always verify changes: after write_file or edit_file, use read_file to confirm the content is correct. For large files, use offset and limit to read specific sections instead of the entire file.
|
|
7571
7593
|
- Use relative paths (relative to workspace). Accessing files outside workspace requires controller approval.
|
|
7594
|
+
- Scratch files and intermediate artifacts go to \`.agents/tmp/\` inside the current task workspace. Never \`/tmp\` \u2014 it sits outside the workspace, so every command there costs an approval \u2014 and never the home directory or the repository root.
|
|
7595
|
+
- Anything worth keeping goes to a normal path in the workspace (it is version-controlled); one-off intermediates stay in \`.agents/tmp/\`.
|
|
7596
|
+
- Before writing, confirm which workspace you are in: \`pwd\`, or the workspace named in the task description \u2014 that one outranks any path you assume.
|
|
7572
7597
|
|
|
7573
7598
|
## Web search
|
|
7574
7599
|
|
|
@@ -7688,6 +7713,7 @@ var RULES = [
|
|
|
7688
7713
|
"Report meaningful progress as you work (entering a new phase, completing a sub-step, recovering from an error).",
|
|
7689
7714
|
"Do NOT output a final summary right before completing \u2014 the system already captures the result separately.",
|
|
7690
7715
|
"Match the language of the task description.",
|
|
7716
|
+
"Keep scratch files and intermediate artifacts in `.agents/tmp/` inside the task workspace \u2014 never `/tmp`, the home directory, or the repository root.",
|
|
7691
7717
|
"</Rules>"
|
|
7692
7718
|
].join("\n");
|
|
7693
7719
|
function buildThirdPartyPrompt(skills) {
|
|
@@ -7880,11 +7906,11 @@ var WorkerAgent = class extends BaseAgent {
|
|
|
7880
7906
|
return this.state.get("conversation") ?? [];
|
|
7881
7907
|
}
|
|
7882
7908
|
// --- Subscribable ---
|
|
7883
|
-
subscribe(
|
|
7884
|
-
return this.state.on(
|
|
7909
|
+
subscribe(path10, callback) {
|
|
7910
|
+
return this.state.on(path10, callback);
|
|
7885
7911
|
}
|
|
7886
|
-
getSnapshot(
|
|
7887
|
-
return this.state.get(
|
|
7912
|
+
getSnapshot(path10) {
|
|
7913
|
+
return this.state.get(path10);
|
|
7888
7914
|
}
|
|
7889
7915
|
/**
|
|
7890
7916
|
* Accept a message from the controller — writes to internal inbox. The
|
|
@@ -7992,7 +8018,7 @@ import { resolve as resolve5 } from "path";
|
|
|
7992
8018
|
|
|
7993
8019
|
// src/infra/permission/policy.ts
|
|
7994
8020
|
import { realpathSync as realpathSync3 } from "fs";
|
|
7995
|
-
import { basename as basename6, dirname as dirname7, isAbsolute, join as
|
|
8021
|
+
import { basename as basename6, dirname as dirname7, isAbsolute, join as join17, resolve as resolve4, sep } from "path";
|
|
7996
8022
|
var DEFAULT_SENSITIVE_RELATIVE_PATHS = [
|
|
7997
8023
|
".ssh",
|
|
7998
8024
|
".gnupg",
|
|
@@ -8005,9 +8031,7 @@ var DEFAULT_SENSITIVE_RELATIVE_PATHS = [
|
|
|
8005
8031
|
".git-credentials",
|
|
8006
8032
|
".npmrc",
|
|
8007
8033
|
".pypirc",
|
|
8008
|
-
".config/gh"
|
|
8009
|
-
".wayang.config.json",
|
|
8010
|
-
".wayang"
|
|
8034
|
+
".config/gh"
|
|
8011
8035
|
];
|
|
8012
8036
|
var SENSITIVE_SEGMENTS = /* @__PURE__ */ new Set([
|
|
8013
8037
|
".ssh",
|
|
@@ -8019,13 +8043,7 @@ var SENSITIVE_SEGMENTS = /* @__PURE__ */ new Set([
|
|
|
8019
8043
|
".netrc",
|
|
8020
8044
|
".git-credentials",
|
|
8021
8045
|
".npmrc",
|
|
8022
|
-
".pypirc"
|
|
8023
|
-
// The wayang data dir (sessions/conversations) — same intent as the
|
|
8024
|
-
// home-anchored `.wayang` entry, but applied wherever it appears.
|
|
8025
|
-
".wayang"
|
|
8026
|
-
]);
|
|
8027
|
-
var SENSITIVE_FILENAMES = /* @__PURE__ */ new Set([
|
|
8028
|
-
".wayang.config.json"
|
|
8046
|
+
".pypirc"
|
|
8029
8047
|
]);
|
|
8030
8048
|
var SENSITIVE_SEGMENT_PAIRS = [
|
|
8031
8049
|
[".docker", "config.json"]
|
|
@@ -8046,7 +8064,7 @@ function resolveDeepestExistingAncestor(lexical) {
|
|
|
8046
8064
|
for (let depth = 0; depth <= MAX_ANCESTOR_DEPTH; depth += 1) {
|
|
8047
8065
|
try {
|
|
8048
8066
|
const resolved = realpathSync3(candidate);
|
|
8049
|
-
return peeled.length === 0 ? resolved : resolve4(
|
|
8067
|
+
return peeled.length === 0 ? resolved : resolve4(join17(resolved, ...peeled));
|
|
8050
8068
|
} catch {
|
|
8051
8069
|
}
|
|
8052
8070
|
const parent = dirname7(candidate);
|
|
@@ -8073,7 +8091,6 @@ function matchSensitiveEntry(absPath) {
|
|
|
8073
8091
|
for (let i = 0; i < segments.length; i += 1) {
|
|
8074
8092
|
const segment = segments[i];
|
|
8075
8093
|
if (SENSITIVE_SEGMENTS.has(segment)) return { entry: segment, source: "segment" };
|
|
8076
|
-
if (SENSITIVE_FILENAMES.has(segment)) return { entry: segment, source: "filename" };
|
|
8077
8094
|
for (const [dir, child] of SENSITIVE_SEGMENT_PAIRS) {
|
|
8078
8095
|
if (segment === dir && segments[i + 1] === child) {
|
|
8079
8096
|
return { entry: `${dir}/${child}`, source: "pair" };
|
|
@@ -8651,10 +8668,10 @@ function extractPathCandidates(segments) {
|
|
|
8651
8668
|
function commandAccess(candidates) {
|
|
8652
8669
|
return candidates.some((candidate) => candidate.access === "write") ? "write" : "unknown";
|
|
8653
8670
|
}
|
|
8654
|
-
function decidePath(scope,
|
|
8671
|
+
function decidePath(scope, path10, access) {
|
|
8655
8672
|
const permission = scope.permission;
|
|
8656
8673
|
return decideAccess({
|
|
8657
|
-
path:
|
|
8674
|
+
path: path10,
|
|
8658
8675
|
access,
|
|
8659
8676
|
cwd: scope.workspace,
|
|
8660
8677
|
homeDir: scope.homeDir,
|
|
@@ -8975,7 +8992,7 @@ var BlockingRequestMap = class {
|
|
|
8975
8992
|
|
|
8976
8993
|
// src/services/agents/claude-code-worker.ts
|
|
8977
8994
|
import { basename as basename7, dirname as dirname8 } from "path";
|
|
8978
|
-
import { homedir } from "os";
|
|
8995
|
+
import { homedir as homedir2 } from "os";
|
|
8979
8996
|
var INQUIRY_STREAM_CLOSE_TIMEOUT_MS = 36e4;
|
|
8980
8997
|
var ClaudeCodeWorker = class _ClaudeCodeWorker {
|
|
8981
8998
|
id;
|
|
@@ -9239,11 +9256,11 @@ var ClaudeCodeWorker = class _ClaudeCodeWorker {
|
|
|
9239
9256
|
return this.state.get("conversation") ?? [];
|
|
9240
9257
|
}
|
|
9241
9258
|
// --- Subscribable ---
|
|
9242
|
-
subscribe(
|
|
9243
|
-
return this.state.on(
|
|
9259
|
+
subscribe(path10, callback) {
|
|
9260
|
+
return this.state.on(path10, callback);
|
|
9244
9261
|
}
|
|
9245
|
-
getSnapshot(
|
|
9246
|
-
return this.state.get(
|
|
9262
|
+
getSnapshot(path10) {
|
|
9263
|
+
return this.state.get(path10);
|
|
9247
9264
|
}
|
|
9248
9265
|
/**
|
|
9249
9266
|
* Accept a message from the controller. The message is queued and handed
|
|
@@ -9331,7 +9348,7 @@ var ClaudeCodeWorker = class _ClaudeCodeWorker {
|
|
|
9331
9348
|
effectiveAccessScope() {
|
|
9332
9349
|
if (this._accessScope) return this._accessScope;
|
|
9333
9350
|
const wayangDir = wayangDirForSessionDir(this.sessionDir);
|
|
9334
|
-
const homeDir2 = wayangDir && basename7(wayangDir) === ".wayang" ? dirname8(wayangDir) :
|
|
9351
|
+
const homeDir2 = wayangDir && basename7(wayangDir) === ".wayang" ? dirname8(wayangDir) : homedir2();
|
|
9335
9352
|
return { homeDir: homeDir2, workspace: this.workspaceDir };
|
|
9336
9353
|
}
|
|
9337
9354
|
/**
|
|
@@ -9808,11 +9825,14 @@ controller: ${answer}`,
|
|
|
9808
9825
|
};
|
|
9809
9826
|
|
|
9810
9827
|
// src/services/tools/add-task.ts
|
|
9828
|
+
import { mkdirSync as mkdirSync7 } from "fs";
|
|
9829
|
+
import { homedir as homedir3 } from "os";
|
|
9830
|
+
import * as path8 from "path";
|
|
9811
9831
|
import { z as z3 } from "zod";
|
|
9812
9832
|
|
|
9813
9833
|
// src/services/tools/workspace-rules.ts
|
|
9814
9834
|
import { readFileSync as readFileSync10, statSync as statSync5 } from "fs";
|
|
9815
|
-
import { join as
|
|
9835
|
+
import { join as join18 } from "path";
|
|
9816
9836
|
var RULE_FILE_NAMES2 = ["AGENTS.md", "CLAUDE.md"];
|
|
9817
9837
|
var WORKSPACE_RULES_MAX_LINES = 40;
|
|
9818
9838
|
var WORKSPACE_RULES_MAX_CHARS = 2e3;
|
|
@@ -9820,7 +9840,7 @@ function buildWorkspaceRulesSummary(input2) {
|
|
|
9820
9840
|
const workspacePath = input2.workspacePath;
|
|
9821
9841
|
if (!workspacePath) return "";
|
|
9822
9842
|
for (const name of RULE_FILE_NAMES2) {
|
|
9823
|
-
const rulesPath =
|
|
9843
|
+
const rulesPath = join18(workspacePath, name);
|
|
9824
9844
|
if (!isRegularFile(rulesPath)) continue;
|
|
9825
9845
|
const header = `Workspace rules (from ${rulesPath}):`;
|
|
9826
9846
|
const refusal = describeRefusal(rulesPath, workspacePath, input2);
|
|
@@ -9893,17 +9913,50 @@ function readTextFile(p) {
|
|
|
9893
9913
|
}
|
|
9894
9914
|
|
|
9895
9915
|
// src/services/tools/add-task.ts
|
|
9916
|
+
var HOME_VAR_PREFIX = /^\$(?:\{HOME\}|HOME)(?=\/|$)/;
|
|
9917
|
+
function expandHomePrefix(raw, homeDir2) {
|
|
9918
|
+
if (raw === "~") return homeDir2;
|
|
9919
|
+
if (raw.startsWith("~/")) return path8.join(homeDir2, raw.slice(2));
|
|
9920
|
+
const match = HOME_VAR_PREFIX.exec(raw);
|
|
9921
|
+
if (match) {
|
|
9922
|
+
const rest = raw.slice(match[0].length);
|
|
9923
|
+
return rest === "" ? homeDir2 : path8.join(homeDir2, rest);
|
|
9924
|
+
}
|
|
9925
|
+
return raw.startsWith("~") ? null : raw;
|
|
9926
|
+
}
|
|
9927
|
+
function ensureWorkspaceDir(dirPath) {
|
|
9928
|
+
try {
|
|
9929
|
+
mkdirSync7(dirPath, { recursive: true });
|
|
9930
|
+
return null;
|
|
9931
|
+
} catch (err) {
|
|
9932
|
+
return `Cannot create workspace ${dirPath}: ${err.message}. The task was NOT created.`;
|
|
9933
|
+
}
|
|
9934
|
+
}
|
|
9896
9935
|
function resolveTaskWorkspace(requested, deps) {
|
|
9897
9936
|
const current = deps.workspaceDir;
|
|
9898
|
-
|
|
9937
|
+
let declared;
|
|
9938
|
+
if (requested !== void 0 && requested.trim() !== "") {
|
|
9939
|
+
const expanded = expandHomePrefix(requested.trim(), deps.homeDir ?? homedir3());
|
|
9940
|
+
if (expanded === null) {
|
|
9941
|
+
return {
|
|
9942
|
+
error: `Cannot resolve workspace "${requested}": only "~" and "$HOME" expand to the home directory, so no readable path was left. The task was NOT created.`
|
|
9943
|
+
};
|
|
9944
|
+
}
|
|
9945
|
+
declared = expanded;
|
|
9946
|
+
}
|
|
9899
9947
|
if (!current) {
|
|
9900
|
-
if (
|
|
9948
|
+
if (declared !== void 0) {
|
|
9901
9949
|
return {
|
|
9902
|
-
error: `Cannot resolve workspace ${
|
|
9950
|
+
error: `Cannot resolve workspace ${normalizeWorkspacePath(declared)}: the current workspace is unknown in this context, so the task was NOT created.`
|
|
9903
9951
|
};
|
|
9904
9952
|
}
|
|
9905
9953
|
return {};
|
|
9906
9954
|
}
|
|
9955
|
+
if (declared !== void 0 && path8.isAbsolute(declared)) {
|
|
9956
|
+
const dirError = ensureWorkspaceDir(declared);
|
|
9957
|
+
if (dirError !== null) return { error: dirError };
|
|
9958
|
+
}
|
|
9959
|
+
const requestedPath = declared !== void 0 ? normalizeWorkspacePath(declared) : void 0;
|
|
9907
9960
|
const targetPath = requestedPath ?? normalizeWorkspacePath(current);
|
|
9908
9961
|
const ref = resolveWorkspaceRef(deps.wayangDir ?? null, targetPath);
|
|
9909
9962
|
return { workspace: { path: ref.path, slug: ref.slug } };
|
|
@@ -9959,6 +10012,7 @@ function withWorkspaceRules(resultText, workspacePath, deps) {
|
|
|
9959
10012
|
${summary}`;
|
|
9960
10013
|
}
|
|
9961
10014
|
function addTaskTool(deps) {
|
|
10015
|
+
const generalWorkspaceNote = deps.generalWorkspaceDir ? ` Tasks that belong to no codebase (research, collecting material, drafting a document) run in the general workspace ${deps.generalWorkspaceDir} \u2014 it is created on first use.` : "";
|
|
9962
10016
|
return defineTool({
|
|
9963
10017
|
description: "Add a new task to the queue. A Worker will execute it automatically.",
|
|
9964
10018
|
parameters: z3.object({
|
|
@@ -9973,7 +10027,7 @@ function addTaskTool(deps) {
|
|
|
9973
10027
|
"Dispatch this task to an existing idle worker (the workerId returned from a prior multiStage task). The worker resumes with its accumulated context. Omit for a fresh worker."
|
|
9974
10028
|
),
|
|
9975
10029
|
workspace: z3.string().optional().describe(
|
|
9976
|
-
|
|
10030
|
+
`Target workspace path; defaults to the current workspace. A different path runs this task there \u2014 the worker, its tools and its permission checks use it as their root. A leading ~ or $HOME expands to the home directory, and a missing directory is created.${generalWorkspaceNote}`
|
|
9977
10031
|
),
|
|
9978
10032
|
allowedDirs: z3.array(z3.string()).optional().describe(
|
|
9979
10033
|
"Extra directories this task may access, in addition to its workspace. Only this task is affected \u2014 the grant is NOT inherited by other tasks. Each entry must be inside the user home and off the denied-path list (credentials, tool config); an invalid entry fails the whole call and no task is created. Paths are normalized; relative entries resolve against the task workspace."
|
|
@@ -10553,8 +10607,8 @@ function readFileTool(deps = {}) {
|
|
|
10553
10607
|
offset: z14.number().int().min(1).optional().describe("Start line number (1-based)"),
|
|
10554
10608
|
limit: z14.number().int().min(1).optional().describe("Number of lines to read")
|
|
10555
10609
|
}),
|
|
10556
|
-
execute: safeExecute("read_file", async ({ path:
|
|
10557
|
-
const resolved = resolve6(workspace,
|
|
10610
|
+
execute: safeExecute("read_file", async ({ path: path10, offset, limit }) => {
|
|
10611
|
+
const resolved = resolve6(workspace, path10);
|
|
10558
10612
|
const content = readFileSync11(resolved, "utf-8");
|
|
10559
10613
|
const lines = content.split("\n");
|
|
10560
10614
|
const totalLines = lines.length;
|
|
@@ -10575,7 +10629,7 @@ function readFileTool(deps = {}) {
|
|
|
10575
10629
|
|
|
10576
10630
|
// src/services/tools/write-file.ts
|
|
10577
10631
|
import { z as z15 } from "zod";
|
|
10578
|
-
import { writeFileSync as writeFileSync4, mkdirSync as
|
|
10632
|
+
import { writeFileSync as writeFileSync4, mkdirSync as mkdirSync8, existsSync as existsSync10 } from "fs";
|
|
10579
10633
|
import { dirname as dirname9, resolve as resolve7 } from "path";
|
|
10580
10634
|
function writeFileTool(deps = {}) {
|
|
10581
10635
|
const workspace = deps.cwd ?? process.cwd();
|
|
@@ -10585,12 +10639,12 @@ function writeFileTool(deps = {}) {
|
|
|
10585
10639
|
path: z15.string().describe("File path (relative to workspace or absolute)"),
|
|
10586
10640
|
content: z15.string().describe("File content")
|
|
10587
10641
|
}),
|
|
10588
|
-
execute: safeExecute("write_file", async ({ path:
|
|
10589
|
-
const resolved = resolve7(workspace,
|
|
10642
|
+
execute: safeExecute("write_file", async ({ path: path10, content }) => {
|
|
10643
|
+
const resolved = resolve7(workspace, path10);
|
|
10590
10644
|
if (existsSync10(resolved)) {
|
|
10591
|
-
return `[ERROR] write_file: File already exists: ${
|
|
10645
|
+
return `[ERROR] write_file: File already exists: ${path10}. Use edit_file to modify existing files.`;
|
|
10592
10646
|
}
|
|
10593
|
-
|
|
10647
|
+
mkdirSync8(dirname9(resolved), { recursive: true });
|
|
10594
10648
|
writeFileSync4(resolved, content, "utf-8");
|
|
10595
10649
|
return `Written ${content.length} chars to ${resolved}`;
|
|
10596
10650
|
})
|
|
@@ -10610,22 +10664,22 @@ function editFileTool(deps) {
|
|
|
10610
10664
|
old_string: z16.string().describe("Exact text to find (must be unique in file)"),
|
|
10611
10665
|
new_string: z16.string().describe("Replacement text")
|
|
10612
10666
|
}),
|
|
10613
|
-
execute: safeExecute("edit_file", async ({ path:
|
|
10614
|
-
const resolved = resolve8(workspace,
|
|
10667
|
+
execute: safeExecute("edit_file", async ({ path: path10, old_string, new_string }) => {
|
|
10668
|
+
const resolved = resolve8(workspace, path10);
|
|
10615
10669
|
let content;
|
|
10616
10670
|
try {
|
|
10617
10671
|
content = readFileSync12(resolved, "utf-8");
|
|
10618
10672
|
} catch {
|
|
10619
|
-
return `[ERROR] edit_file: File not found: ${
|
|
10673
|
+
return `[ERROR] edit_file: File not found: ${path10}`;
|
|
10620
10674
|
}
|
|
10621
10675
|
const count = content.split(old_string).length - 1;
|
|
10622
10676
|
if (count === 0)
|
|
10623
|
-
return `[ERROR] edit_file: old_string not found in ${
|
|
10677
|
+
return `[ERROR] edit_file: old_string not found in ${path10}, read the file content and provide a correct old_string.`;
|
|
10624
10678
|
if (count > 1)
|
|
10625
|
-
return `[ERROR] edit_file: old_string found ${count} times in ${
|
|
10679
|
+
return `[ERROR] edit_file: old_string found ${count} times in ${path10}. Provide more surrounding context to make it unique.`;
|
|
10626
10680
|
const newContent = content.replace(old_string, new_string);
|
|
10627
10681
|
writeFileSync5(resolved, newContent, "utf-8");
|
|
10628
|
-
return `Replaced ${old_string.length} chars with ${new_string.length} chars in ${
|
|
10682
|
+
return `Replaced ${old_string.length} chars with ${new_string.length} chars in ${path10}`;
|
|
10629
10683
|
})
|
|
10630
10684
|
});
|
|
10631
10685
|
}
|
|
@@ -10642,8 +10696,8 @@ function searchFilesTool(deps) {
|
|
|
10642
10696
|
pattern: z17.string().describe('Glob pattern (e.g. "**/*.ts", "src/**/*.test.ts")'),
|
|
10643
10697
|
path: z17.string().optional().describe("Base directory for search (default: workspace root)")
|
|
10644
10698
|
}),
|
|
10645
|
-
execute: safeExecute("search_files", async ({ pattern, path:
|
|
10646
|
-
const basePath =
|
|
10699
|
+
execute: safeExecute("search_files", async ({ pattern, path: path10 }) => {
|
|
10700
|
+
const basePath = path10 ? normalizePath(path10, { base: workspace }) : normalizePath(workspace);
|
|
10647
10701
|
if (homeDir2) {
|
|
10648
10702
|
const { decision, matchedEntry } = decideAccess({
|
|
10649
10703
|
access: "read",
|
|
@@ -10651,7 +10705,7 @@ function searchFilesTool(deps) {
|
|
|
10651
10705
|
cwd: workspace,
|
|
10652
10706
|
path: basePath
|
|
10653
10707
|
});
|
|
10654
|
-
const requested =
|
|
10708
|
+
const requested = path10 ?? workspace;
|
|
10655
10709
|
if (matchedEntry !== void 0 || decision === "deny") {
|
|
10656
10710
|
return `[ERROR] search_files: refusing to search "${requested}" \u2014 the path is on the sensitive list (a protected location).`;
|
|
10657
10711
|
}
|
|
@@ -10686,8 +10740,8 @@ function searchContentTool(deps) {
|
|
|
10686
10740
|
path: z18.string().optional().describe("Directory to search in (default: workspace root)"),
|
|
10687
10741
|
include: z18.string().optional().describe('File pattern to include (e.g. "*.ts", "*.py")')
|
|
10688
10742
|
}),
|
|
10689
|
-
execute: safeExecute("search_content", async ({ query: query2, path:
|
|
10690
|
-
const searchDir =
|
|
10743
|
+
execute: safeExecute("search_content", async ({ query: query2, path: path10, include }) => {
|
|
10744
|
+
const searchDir = path10 ? resolve9(workspace, path10) : workspace;
|
|
10691
10745
|
const cmd = `grep -rn${include ? ` --include=${shellQuote(include)}` : ""} -- ${shellQuote(query2)} ${shellQuote(searchDir)}`;
|
|
10692
10746
|
try {
|
|
10693
10747
|
const output = execSync3(cmd, {
|
|
@@ -10950,6 +11004,7 @@ function createControllerTools(deps) {
|
|
|
10950
11004
|
onTaskDispatched: deps.onTaskDispatched,
|
|
10951
11005
|
workspaceDir: deps.workspaceDir,
|
|
10952
11006
|
wayangDir: deps.wayangDir,
|
|
11007
|
+
generalWorkspaceDir: deps.generalWorkspaceDir,
|
|
10953
11008
|
homeDir: deps.homeDir,
|
|
10954
11009
|
permissionConfig: deps.permissionConfig
|
|
10955
11010
|
}),
|
|
@@ -11208,7 +11263,7 @@ function formatApiCallError(err) {
|
|
|
11208
11263
|
|
|
11209
11264
|
// src/services/task-execute-engine.ts
|
|
11210
11265
|
import { basename as basename8, dirname as dirname10, resolve as resolve10 } from "path";
|
|
11211
|
-
import { homedir as
|
|
11266
|
+
import { homedir as homedir4 } from "os";
|
|
11212
11267
|
var RuntimeState = class extends BaseWayangState {
|
|
11213
11268
|
constructor(initialData) {
|
|
11214
11269
|
super(initialData, []);
|
|
@@ -11219,7 +11274,7 @@ var RuntimeState = class extends BaseWayangState {
|
|
|
11219
11274
|
var PUPPET_WORKER_TYPE = "puppet";
|
|
11220
11275
|
function resolveUserHomeDir(sessionDir) {
|
|
11221
11276
|
const wayangDir = wayangDirForSessionDir(sessionDir);
|
|
11222
|
-
return wayangDir && basename8(wayangDir) === ".wayang" ? dirname10(wayangDir) :
|
|
11277
|
+
return wayangDir && basename8(wayangDir) === ".wayang" ? dirname10(wayangDir) : homedir4();
|
|
11223
11278
|
}
|
|
11224
11279
|
var DEFAULT_IDLE_TIMEOUT_MS = 30 * 60 * 1e3;
|
|
11225
11280
|
var INQUIRY_TIMEOUT_MS = 3e5;
|
|
@@ -11288,15 +11343,15 @@ var TaskExecuteEngine = class {
|
|
|
11288
11343
|
// ---------------------------------------------------------------------------
|
|
11289
11344
|
// Subscribable
|
|
11290
11345
|
// ---------------------------------------------------------------------------
|
|
11291
|
-
subscribe(
|
|
11292
|
-
if (
|
|
11293
|
-
if (
|
|
11294
|
-
throw new Error(`TaskExecuteEngine: unknown subscription path "${
|
|
11346
|
+
subscribe(path10, callback) {
|
|
11347
|
+
if (path10.startsWith("tasks.")) return this.taskState.on(path10, callback);
|
|
11348
|
+
if (path10 === "activeWorkers" || path10.startsWith("activeWorkers.")) return this.workerState.on(path10, callback);
|
|
11349
|
+
throw new Error(`TaskExecuteEngine: unknown subscription path "${path10}"`);
|
|
11295
11350
|
}
|
|
11296
|
-
getSnapshot(
|
|
11297
|
-
if (
|
|
11298
|
-
if (
|
|
11299
|
-
throw new Error(`TaskExecuteEngine: unknown snapshot path "${
|
|
11351
|
+
getSnapshot(path10) {
|
|
11352
|
+
if (path10.startsWith("tasks.")) return this.taskState.get(path10);
|
|
11353
|
+
if (path10 === "activeWorkers" || path10.startsWith("activeWorkers.")) return this.workerState.get(path10);
|
|
11354
|
+
throw new Error(`TaskExecuteEngine: unknown snapshot path "${path10}"`);
|
|
11300
11355
|
}
|
|
11301
11356
|
// ---------------------------------------------------------------------------
|
|
11302
11357
|
// Task management
|
|
@@ -12444,7 +12499,7 @@ var ControllerLoop = class {
|
|
|
12444
12499
|
};
|
|
12445
12500
|
|
|
12446
12501
|
// src/services/agents/controller-state.ts
|
|
12447
|
-
import { join as
|
|
12502
|
+
import { join as join20 } from "path";
|
|
12448
12503
|
|
|
12449
12504
|
// src/services/conversation/conversation-index.ts
|
|
12450
12505
|
function emptyUsage() {
|
|
@@ -12668,8 +12723,8 @@ var ControllerAgentState = class extends BaseWayangState {
|
|
|
12668
12723
|
/** In-memory resolve function for the current inquiry — never persisted. */
|
|
12669
12724
|
inquiryResolver = null;
|
|
12670
12725
|
constructor(ctx) {
|
|
12671
|
-
const jsonFile = new JSONFileHelper(
|
|
12672
|
-
const jsonlFile = new JSONLFileHelper(
|
|
12726
|
+
const jsonFile = new JSONFileHelper(join20(ctx.sessionDir, "runtime-state.json"));
|
|
12727
|
+
const jsonlFile = new JSONLFileHelper(join20(ctx.sessionDir, "conversation.jsonl"));
|
|
12673
12728
|
const initialData = {
|
|
12674
12729
|
runtimeState: {
|
|
12675
12730
|
session: { id: "", startedAt: 0 },
|
|
@@ -12694,9 +12749,9 @@ var ControllerAgentState = class extends BaseWayangState {
|
|
|
12694
12749
|
* shared conversation-envelope module — the single source of truth. This
|
|
12695
12750
|
* override only feeds the in-memory turn index incrementally.
|
|
12696
12751
|
*/
|
|
12697
|
-
append(
|
|
12698
|
-
super.append(
|
|
12699
|
-
if (
|
|
12752
|
+
append(path10, entry) {
|
|
12753
|
+
super.append(path10, entry);
|
|
12754
|
+
if (path10 === "conversation" && this.convIndex) {
|
|
12700
12755
|
this.convIndex.onAppend(entry);
|
|
12701
12756
|
}
|
|
12702
12757
|
}
|
|
@@ -12855,7 +12910,7 @@ function buildSummaryInput(entries, maxChars) {
|
|
|
12855
12910
|
|
|
12856
12911
|
// src/services/agents/controller-agent.ts
|
|
12857
12912
|
import { basename as basename9, dirname as dirname11 } from "path";
|
|
12858
|
-
import { homedir as
|
|
12913
|
+
import { homedir as homedir5 } from "os";
|
|
12859
12914
|
var SUMMARIZER_INPUT_RATIO = 0.5;
|
|
12860
12915
|
var ControllerAgent = class _ControllerAgent extends BaseAgent {
|
|
12861
12916
|
state;
|
|
@@ -12921,7 +12976,13 @@ var ControllerAgent = class _ControllerAgent extends BaseAgent {
|
|
|
12921
12976
|
const state = new ControllerAgentState(ctx);
|
|
12922
12977
|
const recorder = new ToolCallRecorder(ctx.logger);
|
|
12923
12978
|
const wayangDir = wayangDirForSessionDir(ctx.sessionDir);
|
|
12924
|
-
const homeDir2 = wayangDir && basename9(wayangDir) === ".wayang" ? dirname11(wayangDir) :
|
|
12979
|
+
const homeDir2 = wayangDir && basename9(wayangDir) === ".wayang" ? dirname11(wayangDir) : homedir5();
|
|
12980
|
+
const generalWorkspaceDir = resolveGeneralWorkspace(ctx);
|
|
12981
|
+
try {
|
|
12982
|
+
ensureGeneralWorkspace(ctx);
|
|
12983
|
+
} catch {
|
|
12984
|
+
ctx.logger.warn(`could not create the general workspace at ${generalWorkspaceDir}`);
|
|
12985
|
+
}
|
|
12925
12986
|
const controllerRules = loadControllerRules({
|
|
12926
12987
|
homeDir: homeDir2,
|
|
12927
12988
|
cwd: ctx.workspaceDir || process.cwd()
|
|
@@ -12979,7 +13040,8 @@ var ControllerAgent = class _ControllerAgent extends BaseAgent {
|
|
|
12979
13040
|
// Default workspace for tasks created by add_task; a declared workspace
|
|
12980
13041
|
// is stamped instead and the engine executes the task there.
|
|
12981
13042
|
workspaceDir: ctx.workspaceDir,
|
|
12982
|
-
wayangDir
|
|
13043
|
+
wayangDir,
|
|
13044
|
+
generalWorkspaceDir
|
|
12983
13045
|
});
|
|
12984
13046
|
return new _ControllerAgent(ctx, state, provider, tools, skills, recorder, controllerRules);
|
|
12985
13047
|
}
|
|
@@ -13246,11 +13308,11 @@ var ControllerAgent = class _ControllerAgent extends BaseAgent {
|
|
|
13246
13308
|
return this.state.getTurn(turnId);
|
|
13247
13309
|
}
|
|
13248
13310
|
// --- Subscribable ---
|
|
13249
|
-
subscribe(
|
|
13250
|
-
return this.state.on(
|
|
13311
|
+
subscribe(path10, callback) {
|
|
13312
|
+
return this.state.on(path10, callback);
|
|
13251
13313
|
}
|
|
13252
|
-
getSnapshot(
|
|
13253
|
-
return this.state.get(
|
|
13314
|
+
getSnapshot(path10) {
|
|
13315
|
+
return this.state.get(path10);
|
|
13254
13316
|
}
|
|
13255
13317
|
/** Check if context needs compaction. */
|
|
13256
13318
|
needsCompaction() {
|
|
@@ -13579,7 +13641,7 @@ function signalToEntry(sig, ts, sessionId) {
|
|
|
13579
13641
|
|
|
13580
13642
|
// src/services/skills/registry.ts
|
|
13581
13643
|
import { readFileSync as readFileSync13, readdirSync as readdirSync2, statSync as statSync6 } from "fs";
|
|
13582
|
-
import { join as
|
|
13644
|
+
import { join as join21 } from "path";
|
|
13583
13645
|
import { parse as parseYaml } from "yaml";
|
|
13584
13646
|
var SKILL_FILE = "SKILL.md";
|
|
13585
13647
|
function parseFrontmatter(raw) {
|
|
@@ -13614,7 +13676,7 @@ function listResources(skillDir) {
|
|
|
13614
13676
|
const walk = (relDir) => {
|
|
13615
13677
|
let entries;
|
|
13616
13678
|
try {
|
|
13617
|
-
entries = readdirSync2(
|
|
13679
|
+
entries = readdirSync2(join21(skillDir, relDir), { withFileTypes: true });
|
|
13618
13680
|
} catch {
|
|
13619
13681
|
return;
|
|
13620
13682
|
}
|
|
@@ -13661,7 +13723,7 @@ var SkillRegistry = class {
|
|
|
13661
13723
|
if (existing) return existing;
|
|
13662
13724
|
const desc = this.descriptors.get(name);
|
|
13663
13725
|
if (!desc) return void 0;
|
|
13664
|
-
const filePath =
|
|
13726
|
+
const filePath = join21(desc.dir, SKILL_FILE);
|
|
13665
13727
|
let raw;
|
|
13666
13728
|
try {
|
|
13667
13729
|
raw = readFileSync13(filePath, "utf-8");
|
|
@@ -13692,8 +13754,8 @@ var SkillRegistry = class {
|
|
|
13692
13754
|
for (const entry of entries) {
|
|
13693
13755
|
if (!entry.isDirectory()) continue;
|
|
13694
13756
|
if (entry.name.startsWith(".") || entry.name === "node_modules") continue;
|
|
13695
|
-
const skillDir =
|
|
13696
|
-
const skillFile =
|
|
13757
|
+
const skillDir = join21(dir, entry.name);
|
|
13758
|
+
const skillFile = join21(skillDir, SKILL_FILE);
|
|
13697
13759
|
if (!existsAsFile(skillFile)) continue;
|
|
13698
13760
|
const { frontmatter } = safeReadFrontmatter(skillFile);
|
|
13699
13761
|
const description = asString(frontmatter.description);
|
|
@@ -13706,16 +13768,16 @@ var SkillRegistry = class {
|
|
|
13706
13768
|
}
|
|
13707
13769
|
}
|
|
13708
13770
|
};
|
|
13709
|
-
function existsAsFile(
|
|
13771
|
+
function existsAsFile(path10) {
|
|
13710
13772
|
try {
|
|
13711
|
-
return statSync6(
|
|
13773
|
+
return statSync6(path10).isFile();
|
|
13712
13774
|
} catch {
|
|
13713
13775
|
return false;
|
|
13714
13776
|
}
|
|
13715
13777
|
}
|
|
13716
|
-
function safeReadFrontmatter(
|
|
13778
|
+
function safeReadFrontmatter(path10) {
|
|
13717
13779
|
try {
|
|
13718
|
-
return parseFrontmatter(readFileSync13(
|
|
13780
|
+
return parseFrontmatter(readFileSync13(path10, "utf-8"));
|
|
13719
13781
|
} catch {
|
|
13720
13782
|
return { frontmatter: {}, body: "" };
|
|
13721
13783
|
}
|
|
@@ -13723,12 +13785,12 @@ function safeReadFrontmatter(path9) {
|
|
|
13723
13785
|
|
|
13724
13786
|
// src/services/skills/skill-dir.ts
|
|
13725
13787
|
import { existsSync as existsSync11 } from "fs";
|
|
13726
|
-
import { homedir as
|
|
13727
|
-
import { join as
|
|
13788
|
+
import { homedir as homedir6 } from "os";
|
|
13789
|
+
import { join as join22, resolve as resolve11 } from "path";
|
|
13728
13790
|
function resolveSkillDirs(workspaceDir2, configDirs) {
|
|
13729
13791
|
const candidates = [
|
|
13730
|
-
|
|
13731
|
-
|
|
13792
|
+
join22(homedir6(), ".wayang", "skills"),
|
|
13793
|
+
join22(workspaceDir2, ".wayang", "skills"),
|
|
13732
13794
|
...(configDirs ?? []).map((d) => resolve11(d))
|
|
13733
13795
|
];
|
|
13734
13796
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -14055,11 +14117,11 @@ async function runWayangServer(options, io = process) {
|
|
|
14055
14117
|
let nextSubscriptionId = 1;
|
|
14056
14118
|
rpc.register(METHOD.SUBSCRIBE, (params) => {
|
|
14057
14119
|
const target = resolveTarget(params.target);
|
|
14058
|
-
const
|
|
14059
|
-
const data = target.getSnapshot(
|
|
14120
|
+
const path10 = requiredString(params, "path");
|
|
14121
|
+
const data = target.getSnapshot(path10);
|
|
14060
14122
|
const subscriptionId = `sub-${nextSubscriptionId++}`;
|
|
14061
|
-
subscriptions.set(subscriptionId, target.subscribe(
|
|
14062
|
-
rpc.notify(NOTIFY.STATE_EVENT, { subscriptionId, target: params.target, path:
|
|
14123
|
+
subscriptions.set(subscriptionId, target.subscribe(path10, (event) => {
|
|
14124
|
+
rpc.notify(NOTIFY.STATE_EVENT, { subscriptionId, target: params.target, path: path10, data: event.data });
|
|
14063
14125
|
}));
|
|
14064
14126
|
return { subscriptionId, data: data === void 0 ? null : data };
|
|
14065
14127
|
});
|
|
@@ -14240,9 +14302,9 @@ if (result.status === "error") {
|
|
|
14240
14302
|
process.exit(1);
|
|
14241
14303
|
}
|
|
14242
14304
|
var { input, flags } = result;
|
|
14243
|
-
var homeDir =
|
|
14305
|
+
var homeDir = path9.resolve(flags.homeDir ?? (process.env.HOME || "~"));
|
|
14244
14306
|
var workspaceDir = normalizeWorkspacePath(flags.workspaceDir ?? process.cwd());
|
|
14245
|
-
var configPath = flags.config ?
|
|
14307
|
+
var configPath = flags.config ? path9.resolve(flags.config) : path9.join(homeDir, ".wayang.config.json");
|
|
14246
14308
|
function fatal(err) {
|
|
14247
14309
|
if (err instanceof SessionLockedError) {
|
|
14248
14310
|
console.error(formatSessionLockHint(err));
|
|
@@ -14254,7 +14316,7 @@ function fatal(err) {
|
|
|
14254
14316
|
if (input[0] === "server") {
|
|
14255
14317
|
serverMain({
|
|
14256
14318
|
configPath,
|
|
14257
|
-
homeDir:
|
|
14319
|
+
homeDir: path9.join(homeDir, ".wayang"),
|
|
14258
14320
|
workspaceDir,
|
|
14259
14321
|
session: flags.session ?? "new",
|
|
14260
14322
|
injectPreviousTasks: flags.injectPreviousTasks,
|