@rien7/agent-center-host 0.0.0-stage → 0.1.1
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/LICENSE +21 -0
- package/README.md +335 -2
- package/dist/agent-tokens.js +105 -0
- package/dist/center-tools.js +882 -0
- package/dist/cli.js +242 -0
- package/dist/config.js +86 -0
- package/dist/core-client.js +151 -0
- package/dist/credentials.js +117 -0
- package/dist/crew-tools.js +115 -0
- package/dist/driver.js +87 -0
- package/dist/host.js +435 -0
- package/dist/index.js +10 -0
- package/dist/login.js +103 -0
- package/dist/memory.js +163 -0
- package/dist/oar-driver.js +1331 -0
- package/dist/prompt.js +82 -0
- package/dist/tool-titles.js +157 -0
- package/dist/wire.js +6 -0
- package/package.json +46 -4
package/dist/prompt.js
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What the model is told: the system prompt (once per session) and the prefix of each
|
|
3
|
+
* user message (time, page context, memory, the context's domain tools).
|
|
4
|
+
*/
|
|
5
|
+
import { contextDomains, localNow } from "./center-tools.js";
|
|
6
|
+
export function systemPrompt(options) {
|
|
7
|
+
return `你是「${options.name}」,用户在 Agent Center 里的个人助手。${options.role ? `\n你的角色:${options.role}` : ""}
|
|
8
|
+
|
|
9
|
+
## 怎么回答
|
|
10
|
+
- 用中文,简短、直接,像熟人说话。先给结论,再给必要的细节。
|
|
11
|
+
- 不要把 URI、ID、工具名这些内部标识说给用户;用标题、时间、地点这些人看得懂的说法。
|
|
12
|
+
- 你写入数据后,App 会在回复下面显示那条记录的卡片,不用把所有字段再念一遍。
|
|
13
|
+
- 用户的时区是 ${options.timeZone}。每条消息开头的 [时间] 是现在的本地时间;「明天」「下周一」按它算。
|
|
14
|
+
|
|
15
|
+
## 用户的数据(Center)
|
|
16
|
+
需要查或改数据时,先确定是哪个域,再:
|
|
17
|
+
1. tools_search({domain}) 列出那个域的全部工具(或 tools_search({query}) 按关键词找);
|
|
18
|
+
2. tool_describe({name}) 看参数;
|
|
19
|
+
3. tool_call({name, input}) 调用。
|
|
20
|
+
不要猜工具名或参数。today_overview 一次给出某天的日程、提醒和到访地点,问「今天/明天有什么」先用它。消息里 [当前页面的工具] 已经给出了完整参数的工具,可以直接 tool_call。
|
|
21
|
+
|
|
22
|
+
Center 里的域(域名 标题:内容 — 工具,工具全名是「域名_工具」,如 reminder_item_create):
|
|
23
|
+
${options.catalog}
|
|
24
|
+
|
|
25
|
+
## 历史
|
|
26
|
+
想不起来的事(以前聊过什么、让你做过什么)先用 agent_search_query 搜聊天记录,再回答。
|
|
27
|
+
|
|
28
|
+
## 记忆
|
|
29
|
+
工作目录里 memory/MEMORY.md 是你的长期记忆索引,memory/notes/*.md 是笔记,都用文件工具(读/写/改)直接改。
|
|
30
|
+
- 回答前先看记忆(MEMORY.md 有变化时会附在消息开头的 [记忆] 里)。
|
|
31
|
+
- 对话里出现值得长期记住的事(用户的偏好、约定、身边的人、反复提到的事),在这轮结束前写进 memory/notes/ 下合适的文件,并在 MEMORY.md 里加一行索引。
|
|
32
|
+
- 只改 memory/ 下的文件,不碰别的文件。MEMORY.md 保持简短(几十行以内)。${options.parent
|
|
33
|
+
? `
|
|
34
|
+
|
|
35
|
+
## 你是子 agent
|
|
36
|
+
你是「${options.parent}」开的子 agent,专门做它交给你的事。带 [来源] 的消息是它发来的,不是用户本人;用户也可能在 App 里直接和你聊。
|
|
37
|
+
- 做完一件事,最后一段回复就是你的报告,会原样交给「${options.parent}」:写清楚结果(要点、数字、日期),不要寒暄。
|
|
38
|
+
- 你不能再开子 agent。`
|
|
39
|
+
: ""}${options.subagents
|
|
40
|
+
? `
|
|
41
|
+
|
|
42
|
+
## 子 agent
|
|
43
|
+
费时、可以独立做完的事(比如整理一段时间的日程和提醒、把一类备忘归纳成总结)可以交给子 agent:agent_spawn 开一个(名字简短,task 写完整),它在后台做,用户在 App 里能看到它。
|
|
44
|
+
- 这一轮就要结果:agent_wait 等它的报告;不等也行,报告到了会作为一条带 [来源] 的消息送回给你,那时用一两句话告诉用户结果。
|
|
45
|
+
- 要补充或追问用 agent_send;用完不再需要的用 agent_close。简单的事自己做,不要开子 agent。`
|
|
46
|
+
: ""}`;
|
|
47
|
+
}
|
|
48
|
+
/** The text of a turn a host restart cut off, run again. */
|
|
49
|
+
export const interruptedText = (text) => `上一轮被打断,请继续。(host 重启了,你上一轮的回复没说完;用户原来的消息如下)\n\n${text}`;
|
|
50
|
+
const page = {
|
|
51
|
+
today: "今天",
|
|
52
|
+
timeline: "时间线",
|
|
53
|
+
};
|
|
54
|
+
/** `[上下文] …` for the app's chat context (the page or record the user is looking at). */
|
|
55
|
+
export function contextLine(context) {
|
|
56
|
+
const c = context;
|
|
57
|
+
if (!c?.kind)
|
|
58
|
+
return null;
|
|
59
|
+
if (c.kind.type === "page") {
|
|
60
|
+
const name = page[c.kind.page ?? ""] ?? c.label ?? c.kind.page;
|
|
61
|
+
return `[上下文] 用户正在看「${name}」页${c.kind.day ? `(${c.kind.day})` : ""}`;
|
|
62
|
+
}
|
|
63
|
+
if (c.kind.type === "entity" && c.kind.uri?.startsWith("agent:conversation/"))
|
|
64
|
+
return `[来源] ${c.label ?? "另一个 agent"}(不是用户本人发的)`;
|
|
65
|
+
if (c.kind.type === "entity")
|
|
66
|
+
return `[上下文] 用户正在看一条记录「${c.label ?? ""}」(${c.kind.uri},可用 core_entity_get 读取)`;
|
|
67
|
+
return null;
|
|
68
|
+
}
|
|
69
|
+
export function turnText(text, parts) {
|
|
70
|
+
const lines = [
|
|
71
|
+
`[时间] ${localNow(parts.timeZone, parts.now)}(${parts.timeZone})`,
|
|
72
|
+
];
|
|
73
|
+
const context = contextLine(parts.context);
|
|
74
|
+
if (context)
|
|
75
|
+
lines.push(context);
|
|
76
|
+
if (parts.memory)
|
|
77
|
+
lines.push(`[记忆] memory/MEMORY.md:\n${parts.memory.trim()}\n[/记忆]`);
|
|
78
|
+
if (parts.tools)
|
|
79
|
+
lines.push(`[当前页面的工具]\n${parts.tools}\n[/当前页面的工具]`);
|
|
80
|
+
return `${lines.join("\n")}\n\n${text}`;
|
|
81
|
+
}
|
|
82
|
+
export { contextDomains };
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* User-facing Chinese titles for tool calls. Core tools are named `<domain>_<noun>_<verb>`
|
|
3
|
+
* (op name with dots → underscores). Runtimes prefix MCP tools with the server name
|
|
4
|
+
* (`mcp__center__tool_call`), and `tool_call` wraps the real tool, so a call is titled
|
|
5
|
+
* by the inner name: 「新建提醒」, never 「tool_call」.
|
|
6
|
+
*/
|
|
7
|
+
export const domainTitles = {
|
|
8
|
+
reminder: "提醒",
|
|
9
|
+
calendar: "日程",
|
|
10
|
+
memo: "备忘",
|
|
11
|
+
people: "联系人",
|
|
12
|
+
location: "地点",
|
|
13
|
+
ledger: "账本",
|
|
14
|
+
inbox: "通知",
|
|
15
|
+
telemetry: "健康",
|
|
16
|
+
agent: "助手",
|
|
17
|
+
core: "通用",
|
|
18
|
+
};
|
|
19
|
+
/** Exact titles for the common tools. */
|
|
20
|
+
const exact = {
|
|
21
|
+
// host-served
|
|
22
|
+
tools_search: "查找工具",
|
|
23
|
+
tool_describe: "查看工具说明",
|
|
24
|
+
today_overview: "查看今天概况",
|
|
25
|
+
agent_search_query: "搜索聊天记录",
|
|
26
|
+
agent_spawn: "开子 agent",
|
|
27
|
+
agent_send: "给子 agent 发消息",
|
|
28
|
+
agent_wait: "等子 agent 的报告",
|
|
29
|
+
agent_close: "结束子 agent",
|
|
30
|
+
// core
|
|
31
|
+
reminder_item_list: "查询提醒",
|
|
32
|
+
reminder_item_create: "新建提醒",
|
|
33
|
+
reminder_item_update: "修改提醒",
|
|
34
|
+
reminder_item_complete: "完成提醒",
|
|
35
|
+
reminder_item_uncomplete: "重新打开提醒",
|
|
36
|
+
reminder_item_delete: "删除提醒",
|
|
37
|
+
calendar_event_list: "查询日程",
|
|
38
|
+
calendar_event_create: "新建日程",
|
|
39
|
+
calendar_event_update: "修改日程",
|
|
40
|
+
calendar_event_delete: "删除日程",
|
|
41
|
+
memo_note_list: "查询备忘",
|
|
42
|
+
memo_note_create: "新建备忘",
|
|
43
|
+
memo_note_update: "修改备忘",
|
|
44
|
+
memo_note_delete: "删除备忘",
|
|
45
|
+
people_person_list: "查询联系人",
|
|
46
|
+
location_visit_list: "查询到访记录",
|
|
47
|
+
location_visit_days: "查询到访日期",
|
|
48
|
+
location_place_search: "查找地点",
|
|
49
|
+
location_place_nearby: "查找附近地点",
|
|
50
|
+
location_search_query: "搜索地点",
|
|
51
|
+
core_timeline_search: "搜索时间线",
|
|
52
|
+
core_timeline_list: "查询时间线",
|
|
53
|
+
core_entity_get: "读取记录",
|
|
54
|
+
core_link_list: "查询关联",
|
|
55
|
+
core_review_list: "查询待确认事项",
|
|
56
|
+
agent_message_list: "读取聊天记录",
|
|
57
|
+
agent_conversation_list: "查询对话",
|
|
58
|
+
ledger_transaction_list: "查询账单",
|
|
59
|
+
ledger_report_summary: "查看账本汇总",
|
|
60
|
+
telemetry_sleep_summary: "查看睡眠",
|
|
61
|
+
telemetry_steps_metric: "查看步数",
|
|
62
|
+
};
|
|
63
|
+
const verbs = [
|
|
64
|
+
[/_(create|add)$/, "新建"],
|
|
65
|
+
[/_(update|set|move)$/, "修改"],
|
|
66
|
+
[/_(delete|remove|purge)$/, "删除"],
|
|
67
|
+
[/_complete$/, "完成"],
|
|
68
|
+
[/_(search|query)$/, "搜索"],
|
|
69
|
+
[
|
|
70
|
+
/_(list|get|getMany|summary|metric|series|stats|days|bounds|balance)$/,
|
|
71
|
+
"查询",
|
|
72
|
+
],
|
|
73
|
+
];
|
|
74
|
+
/** Built-in runtime tools (pi: read/edit/write; Claude Code: Read/Edit/Write/…). */
|
|
75
|
+
function builtinTitle(name, input) {
|
|
76
|
+
const path = String(input?.path ?? input?.file_path ?? input?.filePath ?? "");
|
|
77
|
+
const memory = /(^|\/)memory\//.test(path) || /MEMORY\.md$/.test(path);
|
|
78
|
+
switch (name.toLowerCase()) {
|
|
79
|
+
case "read":
|
|
80
|
+
return memory ? "读取记忆" : "读取文件";
|
|
81
|
+
case "write":
|
|
82
|
+
return memory ? "写入记忆" : "写入文件";
|
|
83
|
+
case "edit":
|
|
84
|
+
case "multiedit":
|
|
85
|
+
return memory ? "更新记忆" : "修改文件";
|
|
86
|
+
case "ls":
|
|
87
|
+
case "glob":
|
|
88
|
+
case "find":
|
|
89
|
+
case "grep":
|
|
90
|
+
return "查找文件";
|
|
91
|
+
case "bash":
|
|
92
|
+
return "运行命令";
|
|
93
|
+
case "todowrite":
|
|
94
|
+
return "整理待办";
|
|
95
|
+
default:
|
|
96
|
+
return null;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
/** Drop a runtime's MCP prefix: `mcp__center__x` → `x`. */
|
|
100
|
+
export const bareToolName = (name) => name.replace(/^mcp__[A-Za-z0-9-]+__/, "");
|
|
101
|
+
/** Fallback for a core tool without an exact title: by verb, then by domain. */
|
|
102
|
+
export function coreToolTitle(name, readOnly) {
|
|
103
|
+
if (exact[name])
|
|
104
|
+
return exact[name];
|
|
105
|
+
const domain = name.split("_")[0] ?? "";
|
|
106
|
+
const subject = domainTitles[domain] ?? domain;
|
|
107
|
+
for (const [pattern, verb] of verbs)
|
|
108
|
+
if (pattern.test(name))
|
|
109
|
+
return `${verb}${subject}`;
|
|
110
|
+
return readOnly === false ? `修改${subject}数据` : `读取${subject}数据`;
|
|
111
|
+
}
|
|
112
|
+
export function toolTitle(name, input = null) {
|
|
113
|
+
const bare = bareToolName(name);
|
|
114
|
+
if (bare !== name || exact[bare] || bare.includes("_"))
|
|
115
|
+
return coreToolTitle(bare);
|
|
116
|
+
return builtinTitle(bare, input) ?? "使用工具";
|
|
117
|
+
}
|
|
118
|
+
const clip = (text, max) => {
|
|
119
|
+
const chars = Array.from(text.replace(/\s+/g, " ").trim());
|
|
120
|
+
return chars.length > max
|
|
121
|
+
? `${chars.slice(0, max).join("")}…`
|
|
122
|
+
: chars.join("");
|
|
123
|
+
};
|
|
124
|
+
/** A one-line summary of a call's input for the step detail. */
|
|
125
|
+
export function inputDetail(input, max = 60) {
|
|
126
|
+
if (input === null || input === undefined)
|
|
127
|
+
return null;
|
|
128
|
+
if (typeof input !== "object")
|
|
129
|
+
return clip(String(input), max);
|
|
130
|
+
const o = input;
|
|
131
|
+
for (const key of [
|
|
132
|
+
"title",
|
|
133
|
+
"q",
|
|
134
|
+
"query",
|
|
135
|
+
"name",
|
|
136
|
+
"agent",
|
|
137
|
+
"text",
|
|
138
|
+
"body",
|
|
139
|
+
"path",
|
|
140
|
+
"file_path",
|
|
141
|
+
"date",
|
|
142
|
+
"domain",
|
|
143
|
+
])
|
|
144
|
+
if (typeof o[key] === "string" && o[key])
|
|
145
|
+
return clip(o[key], max);
|
|
146
|
+
// A time range: 11月4日, or 11月4日–11月6日 (the dates as written, no zone math).
|
|
147
|
+
const day = (v) => {
|
|
148
|
+
const m = typeof v === "string" && /^\d{4}-(\d{2})-(\d{2})/.exec(v);
|
|
149
|
+
return m ? `${Number(m[1])}月${Number(m[2])}日` : null;
|
|
150
|
+
};
|
|
151
|
+
const from = day(o.from ?? o.start);
|
|
152
|
+
const to = day(o.to ?? o.end);
|
|
153
|
+
if (from || to)
|
|
154
|
+
return from && to && from !== to ? `${from}–${to}` : (from ?? `到 ${to}`);
|
|
155
|
+
const json = JSON.stringify(o);
|
|
156
|
+
return json === "{}" ? null : clip(json, max);
|
|
157
|
+
}
|
package/dist/wire.js
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Wire shapes of the agent domain as the host sees them (domains/agent/src/contract.ts).
|
|
3
|
+
* Kept here as plain types so the host has no dependency on the server packages; the
|
|
4
|
+
* integration test runs against the real server, so drift shows up there.
|
|
5
|
+
*/
|
|
6
|
+
export {};
|
package/package.json
CHANGED
|
@@ -1,6 +1,48 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rien7/agent-center-host",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"description": "
|
|
6
|
-
|
|
3
|
+
"version": "0.1.1",
|
|
4
|
+
"license": "MIT",
|
|
5
|
+
"description": "Agent host for Agent Center: claims queued turns and answers them through a driver (an oar runtime: Claude Code, pi; or echo). Node 24+.",
|
|
6
|
+
"keywords": [
|
|
7
|
+
"agent-center",
|
|
8
|
+
"agent",
|
|
9
|
+
"claude-code",
|
|
10
|
+
"oar"
|
|
11
|
+
],
|
|
12
|
+
"homepage": "https://github.com/rien7/agent-center/tree/main/packages/agent-host#readme",
|
|
13
|
+
"repository": {
|
|
14
|
+
"type": "git",
|
|
15
|
+
"url": "git+https://github.com/rien7/agent-center.git",
|
|
16
|
+
"directory": "packages/agent-host"
|
|
17
|
+
},
|
|
18
|
+
"bugs": "https://github.com/rien7/agent-center/issues",
|
|
19
|
+
"type": "module",
|
|
20
|
+
"engines": {
|
|
21
|
+
"node": ">=24"
|
|
22
|
+
},
|
|
23
|
+
"bin": {
|
|
24
|
+
"agent-center-host": "dist/cli.js",
|
|
25
|
+
"agent-host": "dist/cli.js"
|
|
26
|
+
},
|
|
27
|
+
"exports": {
|
|
28
|
+
".": "./dist/index.js"
|
|
29
|
+
},
|
|
30
|
+
"files": [
|
|
31
|
+
"dist",
|
|
32
|
+
"README.md"
|
|
33
|
+
],
|
|
34
|
+
"scripts": {
|
|
35
|
+
"start": "node src/cli.ts",
|
|
36
|
+
"build": "rm -rf dist && tsc -p tsconfig.build.json",
|
|
37
|
+
"prepublishOnly": "npm run build",
|
|
38
|
+
"typecheck": "tsc -p .",
|
|
39
|
+
"test": "bun test"
|
|
40
|
+
},
|
|
41
|
+
"devDependencies": {
|
|
42
|
+
"@agent-center/agent": "workspace:*",
|
|
43
|
+
"@agent-center/kit": "workspace:*"
|
|
44
|
+
},
|
|
45
|
+
"dependencies": {
|
|
46
|
+
"@botiverse/oar": "0.86.0"
|
|
47
|
+
}
|
|
48
|
+
}
|