agent-sin 0.1.12 → 0.1.15
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/CHANGELOG.md +66 -0
- package/README.md +2 -1
- package/builtin-skills/_shared/_todo_lib.py +290 -0
- package/builtin-skills/even-g2-setup/main.ts +896 -0
- package/builtin-skills/even-g2-setup/skill.yaml +133 -0
- package/builtin-skills/memo-delete/main.py +28 -107
- package/builtin-skills/memo-delete/skill.yaml +10 -21
- package/builtin-skills/memo-index/main.py +96 -64
- package/builtin-skills/memo-index/skill.yaml +4 -10
- package/builtin-skills/memo-list/main.py +126 -72
- package/builtin-skills/memo-list/skill.yaml +8 -14
- package/builtin-skills/memo-save/main.py +191 -25
- package/builtin-skills/memo-save/skill.yaml +29 -5
- package/builtin-skills/memo-search/main.py +38 -18
- package/builtin-skills/memo-vector-search/main.py +11 -6
- package/builtin-skills/nightly-topic-knowledge/_feedback_lib.py +391 -0
- package/builtin-skills/nightly-topic-knowledge/_topics_lib.py +415 -0
- package/builtin-skills/nightly-topic-knowledge/main.py +403 -0
- package/builtin-skills/nightly-topic-knowledge/skill.yaml +88 -0
- package/builtin-skills/schedule-add/main.py +26 -0
- package/builtin-skills/service-restart/main.ts +249 -0
- package/builtin-skills/service-restart/skill.yaml +49 -0
- package/builtin-skills/todo-add/main.py +3 -1
- package/builtin-skills/todo-delete/main.py +3 -1
- package/builtin-skills/todo-done/main.py +3 -1
- package/builtin-skills/todo-list/main.py +4 -1
- package/builtin-skills/todo-tick/main.py +3 -1
- package/builtin-skills/topic-knowledge-read/main.py +118 -0
- package/builtin-skills/topic-knowledge-read/skill.yaml +49 -0
- package/dist/builder/build-action-classifier.d.ts +18 -0
- package/dist/builder/build-action-classifier.js +82 -1
- package/dist/builder/build-flow.d.ts +33 -4
- package/dist/builder/build-flow.js +251 -89
- package/dist/builder/builder-session.d.ts +1 -1
- package/dist/builder/builder-session.js +112 -7
- package/dist/builder/conversation-router.d.ts +4 -2
- package/dist/builder/conversation-router.js +19 -2
- package/dist/cli/index.js +323 -20
- package/dist/core/ai-provider.d.ts +1 -0
- package/dist/core/ai-provider.js +8 -3
- package/dist/core/chat-engine.d.ts +9 -3
- package/dist/core/chat-engine.js +1263 -146
- package/dist/core/config.d.ts +4 -0
- package/dist/core/config.js +82 -0
- package/dist/core/daily-memory-promotion.d.ts +7 -0
- package/dist/core/daily-memory-promotion.js +568 -14
- package/dist/core/image-attachments.d.ts +31 -0
- package/dist/core/image-attachments.js +237 -0
- package/dist/core/logger.d.ts +2 -1
- package/dist/core/logger.js +77 -1
- package/dist/core/memo-migration.d.ts +3 -0
- package/dist/core/memo-migration.js +422 -0
- package/dist/core/native-modules.d.ts +24 -0
- package/dist/core/native-modules.js +99 -0
- package/dist/core/notifier.d.ts +8 -3
- package/dist/core/notifier.js +191 -17
- package/dist/core/obsidian-vault.d.ts +19 -0
- package/dist/core/obsidian-vault.js +477 -0
- package/dist/core/operating-model.d.ts +2 -0
- package/dist/core/operating-model.js +15 -0
- package/dist/core/output-writer.d.ts +3 -2
- package/dist/core/output-writer.js +108 -7
- package/dist/core/profile-memory.js +22 -1
- package/dist/core/runtime.d.ts +2 -0
- package/dist/core/runtime.js +9 -1
- package/dist/core/secrets.d.ts +4 -0
- package/dist/core/secrets.js +34 -0
- package/dist/core/skill-history.d.ts +44 -0
- package/dist/core/skill-history.js +329 -0
- package/dist/core/skill-registry.d.ts +5 -0
- package/dist/core/skill-registry.js +11 -0
- package/dist/discord/bot.d.ts +1 -0
- package/dist/discord/bot.js +181 -10
- package/dist/even-g2/gateway.d.ts +15 -0
- package/dist/even-g2/gateway.js +868 -0
- package/dist/runtimes/codex-app-server.d.ts +5 -1
- package/dist/runtimes/codex-app-server.js +147 -8
- package/dist/runtimes/python-runner.js +82 -0
- package/dist/runtimes/typescript-runner.js +13 -1
- package/dist/skills-sdk/types.d.ts +19 -4
- package/dist/telegram/bot.d.ts +1 -0
- package/dist/telegram/bot.js +115 -7
- package/package.json +3 -1
- package/templates/even-g2-agent/README.md +83 -0
- package/templates/even-g2-agent/app.json +20 -0
- package/templates/even-g2-agent/index.html +31 -0
- package/templates/even-g2-agent/package-lock.json +1836 -0
- package/templates/even-g2-agent/package.json +22 -0
- package/templates/even-g2-agent/scripts/qr-auto.mjs +182 -0
- package/templates/even-g2-agent/src/embedded-config.ts +4 -0
- package/templates/even-g2-agent/src/main.ts +539 -0
- package/templates/even-g2-agent/src/style.css +70 -0
- package/templates/even-g2-agent/tsconfig.json +11 -0
- package/templates/skill-python/main.py +20 -2
- package/templates/skill-python/skill.yaml +9 -0
- package/templates/skill-typescript/main.ts +40 -5
- package/templates/skill-typescript/skill.yaml +9 -0
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
import { spawn, execFile } from "node:child_process";
|
|
2
|
+
import { access } from "node:fs/promises";
|
|
3
|
+
import os from "node:os";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
|
|
6
|
+
const DARWIN_LABEL = "com.agent-sin.gateway";
|
|
7
|
+
const WINDOWS_TASK_NAME = "Agent-Sin Gateway";
|
|
8
|
+
const DEFAULT_DELAY_MS = 12000;
|
|
9
|
+
|
|
10
|
+
type SkillInput = {
|
|
11
|
+
args?: Record<string, unknown>;
|
|
12
|
+
sources?: {
|
|
13
|
+
workspace?: string;
|
|
14
|
+
locale?: string;
|
|
15
|
+
};
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
type SkillResult = {
|
|
19
|
+
status: "ok" | "error" | "skipped";
|
|
20
|
+
title: string;
|
|
21
|
+
summary: string;
|
|
22
|
+
outputs: Record<string, unknown>;
|
|
23
|
+
data: Record<string, unknown>;
|
|
24
|
+
suggestions: unknown[];
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
export async function run(_ctx: unknown, input: SkillInput): Promise<SkillResult> {
|
|
28
|
+
const locale = input.sources?.locale === "ja" ? "ja" : "en";
|
|
29
|
+
const workspace = input.sources?.workspace || process.cwd();
|
|
30
|
+
const delayMs = clampDelay(input.args?.delay_ms);
|
|
31
|
+
|
|
32
|
+
if (process.platform === "darwin") {
|
|
33
|
+
return scheduleDarwinRestart(locale, workspace, delayMs);
|
|
34
|
+
}
|
|
35
|
+
if (process.platform === "win32") {
|
|
36
|
+
return scheduleWindowsRestart(locale, workspace, delayMs);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return errorResult(
|
|
40
|
+
locale,
|
|
41
|
+
"Restart is not supported",
|
|
42
|
+
"自動再起動は未対応です",
|
|
43
|
+
"Automatic service restart is supported on macOS and Windows. Run `agent-sin service restart` from a terminal on this platform.",
|
|
44
|
+
"この環境では自動再起動に未対応です。ターミナルで `agent-sin service restart` を実行してください。",
|
|
45
|
+
{ platform: process.platform },
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
async function scheduleDarwinRestart(locale: "en" | "ja", workspace: string, delayMs: number): Promise<SkillResult> {
|
|
50
|
+
const plistPath = path.join(os.homedir(), "Library", "LaunchAgents", `${DARWIN_LABEL}.plist`);
|
|
51
|
+
try {
|
|
52
|
+
await access(plistPath);
|
|
53
|
+
} catch {
|
|
54
|
+
return errorResult(
|
|
55
|
+
locale,
|
|
56
|
+
"Service is not installed",
|
|
57
|
+
"サービス未登録",
|
|
58
|
+
"Agent-Sin service is not installed. Run `agent-sin service install` first.",
|
|
59
|
+
"Agent-Sin service が登録されていません。先に `agent-sin service install` を実行してください。",
|
|
60
|
+
{ platform: "darwin", plist_path: plistPath },
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const uid = typeof process.getuid === "function" ? process.getuid() : os.userInfo().uid;
|
|
65
|
+
const target = `gui/${uid}/${DARWIN_LABEL}`;
|
|
66
|
+
const loaded = await commandSucceeds("launchctl", ["print", target]);
|
|
67
|
+
const helperScript = loaded ? darwinKickstartScript() : cliRestartScript();
|
|
68
|
+
const helperEnv = loaded
|
|
69
|
+
? { AGENT_SIN_RESTART_DELAY_MS: String(delayMs), AGENT_SIN_SERVICE_TARGET: target }
|
|
70
|
+
: cliRestartEnv(workspace, delayMs);
|
|
71
|
+
|
|
72
|
+
try {
|
|
73
|
+
await spawnDetachedHelper(helperScript, helperEnv, workspace);
|
|
74
|
+
} catch (error) {
|
|
75
|
+
return errorResult(
|
|
76
|
+
locale,
|
|
77
|
+
"Restart failed",
|
|
78
|
+
"再起動できませんでした",
|
|
79
|
+
`Could not schedule restart: ${errorMessage(error)}`,
|
|
80
|
+
`再起動を予約できませんでした: ${errorMessage(error)}`,
|
|
81
|
+
{ platform: "darwin" },
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return okResult(locale, delayMs, loaded ? "launchctl-kickstart" : "service-restart");
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
async function scheduleWindowsRestart(locale: "en" | "ja", workspace: string, delayMs: number): Promise<SkillResult> {
|
|
89
|
+
const installed = await commandSucceeds("schtasks", ["/Query", "/TN", WINDOWS_TASK_NAME]);
|
|
90
|
+
if (!installed) {
|
|
91
|
+
return errorResult(
|
|
92
|
+
locale,
|
|
93
|
+
"Service is not installed",
|
|
94
|
+
"サービス未登録",
|
|
95
|
+
"Agent-Sin service is not installed. Run `agent-sin service install` first.",
|
|
96
|
+
"Agent-Sin service が登録されていません。先に `agent-sin service install` を実行してください。",
|
|
97
|
+
{ platform: "win32", task_name: WINDOWS_TASK_NAME },
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
try {
|
|
102
|
+
await spawnDetachedHelper(windowsRestartScript(), {
|
|
103
|
+
AGENT_SIN_RESTART_DELAY_MS: String(delayMs),
|
|
104
|
+
AGENT_SIN_WINDOWS_TASK_NAME: WINDOWS_TASK_NAME,
|
|
105
|
+
}, workspace);
|
|
106
|
+
} catch (error) {
|
|
107
|
+
return errorResult(
|
|
108
|
+
locale,
|
|
109
|
+
"Restart failed",
|
|
110
|
+
"再起動できませんでした",
|
|
111
|
+
`Could not schedule restart: ${errorMessage(error)}`,
|
|
112
|
+
`再起動を予約できませんでした: ${errorMessage(error)}`,
|
|
113
|
+
{ platform: "win32" },
|
|
114
|
+
);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
return okResult(locale, delayMs, "schtasks-restart");
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function darwinKickstartScript(): string {
|
|
121
|
+
return `
|
|
122
|
+
const { spawn } = require("node:child_process");
|
|
123
|
+
const delay = Number(process.env.AGENT_SIN_RESTART_DELAY_MS || ${DEFAULT_DELAY_MS});
|
|
124
|
+
const target = process.env.AGENT_SIN_SERVICE_TARGET;
|
|
125
|
+
setTimeout(() => {
|
|
126
|
+
const child = spawn("launchctl", ["kickstart", "-k", target], { detached: true, stdio: "ignore" });
|
|
127
|
+
child.unref();
|
|
128
|
+
}, delay);
|
|
129
|
+
setTimeout(() => process.exit(0), delay + 2000);
|
|
130
|
+
`;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function windowsRestartScript(): string {
|
|
134
|
+
return `
|
|
135
|
+
const { spawn } = require("node:child_process");
|
|
136
|
+
const delay = Number(process.env.AGENT_SIN_RESTART_DELAY_MS || ${DEFAULT_DELAY_MS});
|
|
137
|
+
const taskName = process.env.AGENT_SIN_WINDOWS_TASK_NAME;
|
|
138
|
+
function run(args) {
|
|
139
|
+
return new Promise((resolve) => {
|
|
140
|
+
const child = spawn("schtasks", args, { stdio: "ignore", windowsHide: true });
|
|
141
|
+
child.on("error", () => resolve());
|
|
142
|
+
child.on("exit", () => resolve());
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
setTimeout(async () => {
|
|
146
|
+
await run(["/End", "/TN", taskName]);
|
|
147
|
+
setTimeout(async () => {
|
|
148
|
+
await run(["/Run", "/TN", taskName]);
|
|
149
|
+
process.exit(0);
|
|
150
|
+
}, 1000);
|
|
151
|
+
}, delay);
|
|
152
|
+
setTimeout(() => process.exit(0), delay + 15000);
|
|
153
|
+
`;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
function cliRestartScript(): string {
|
|
157
|
+
return `
|
|
158
|
+
const { spawn } = require("node:child_process");
|
|
159
|
+
const delay = Number(process.env.AGENT_SIN_RESTART_DELAY_MS || ${DEFAULT_DELAY_MS});
|
|
160
|
+
const cli = process.env.AGENT_SIN_CLI_PATH;
|
|
161
|
+
const workspace = process.env.AGENT_SIN_WORKSPACE || process.cwd();
|
|
162
|
+
setTimeout(() => {
|
|
163
|
+
const child = spawn(process.execPath, [cli, "service", "restart"], {
|
|
164
|
+
detached: true,
|
|
165
|
+
stdio: "ignore",
|
|
166
|
+
cwd: workspace,
|
|
167
|
+
env: process.env,
|
|
168
|
+
windowsHide: true,
|
|
169
|
+
});
|
|
170
|
+
child.unref();
|
|
171
|
+
}, delay);
|
|
172
|
+
setTimeout(() => process.exit(0), delay + 5000);
|
|
173
|
+
`;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
function cliRestartEnv(workspace: string, delayMs: number): Record<string, string> {
|
|
177
|
+
return {
|
|
178
|
+
AGENT_SIN_RESTART_DELAY_MS: String(delayMs),
|
|
179
|
+
AGENT_SIN_CLI_PATH: process.argv[1] || "agent-sin",
|
|
180
|
+
AGENT_SIN_WORKSPACE: workspace,
|
|
181
|
+
AGENT_SIN_HOME: process.env.AGENT_SIN_HOME || workspace,
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
function spawnDetachedHelper(script: string, extraEnv: Record<string, string>, cwd: string): Promise<void> {
|
|
186
|
+
return new Promise((resolve, reject) => {
|
|
187
|
+
const child = spawn(process.execPath, ["-e", script], {
|
|
188
|
+
detached: true,
|
|
189
|
+
stdio: "ignore",
|
|
190
|
+
cwd,
|
|
191
|
+
env: { ...process.env, ...extraEnv },
|
|
192
|
+
windowsHide: true,
|
|
193
|
+
});
|
|
194
|
+
child.once("error", reject);
|
|
195
|
+
child.once("spawn", () => {
|
|
196
|
+
child.unref();
|
|
197
|
+
resolve();
|
|
198
|
+
});
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
function commandSucceeds(command: string, args: string[]): Promise<boolean> {
|
|
203
|
+
return new Promise((resolve) => {
|
|
204
|
+
execFile(command, args, { windowsHide: true }, (error) => {
|
|
205
|
+
resolve(!error);
|
|
206
|
+
});
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
function clampDelay(value: unknown): number {
|
|
211
|
+
const parsed = Number(value);
|
|
212
|
+
if (!Number.isFinite(parsed)) return DEFAULT_DELAY_MS;
|
|
213
|
+
return Math.max(DEFAULT_DELAY_MS, Math.min(30000, Math.floor(parsed)));
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
function okResult(locale: "en" | "ja", delayMs: number, action: string): SkillResult {
|
|
217
|
+
return {
|
|
218
|
+
status: "ok",
|
|
219
|
+
title: locale === "ja" ? "再起動します" : "Restarting",
|
|
220
|
+
summary: locale === "ja"
|
|
221
|
+
? "Agent-Sinを再起動します。数秒後に一度切断され、再接続します。続きの確認は再接続後にもう一度送ってください。"
|
|
222
|
+
: "Restarting Agent-Sin. It will disconnect briefly and reconnect in a few seconds. Send the next check again after it reconnects.",
|
|
223
|
+
outputs: {},
|
|
224
|
+
data: { delay_ms: delayMs, action },
|
|
225
|
+
suggestions: [],
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
function errorResult(
|
|
230
|
+
locale: "en" | "ja",
|
|
231
|
+
titleEn: string,
|
|
232
|
+
titleJa: string,
|
|
233
|
+
summaryEn: string,
|
|
234
|
+
summaryJa: string,
|
|
235
|
+
data: Record<string, unknown>,
|
|
236
|
+
): SkillResult {
|
|
237
|
+
return {
|
|
238
|
+
status: "error",
|
|
239
|
+
title: locale === "ja" ? titleJa : titleEn,
|
|
240
|
+
summary: locale === "ja" ? summaryJa : summaryEn,
|
|
241
|
+
outputs: {},
|
|
242
|
+
data,
|
|
243
|
+
suggestions: [],
|
|
244
|
+
};
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
function errorMessage(error: unknown): string {
|
|
248
|
+
return error instanceof Error ? error.message : String(error);
|
|
249
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# Builtin: service-restart
|
|
2
|
+
# Agent-Sin の常駐サービスを再起動する。Discord / Telegram から設定反映が必要なときに使う。
|
|
3
|
+
|
|
4
|
+
id: service-restart
|
|
5
|
+
name: Agent-Sin Restart
|
|
6
|
+
name_i18n:
|
|
7
|
+
en: Agent-Sin Restart
|
|
8
|
+
ja: Agent-Sin再起動
|
|
9
|
+
description: Agent-Sinの常駐サービスを再起動する
|
|
10
|
+
description_i18n:
|
|
11
|
+
en: Restart the Agent-Sin background service
|
|
12
|
+
ja: Agent-Sinの常駐サービスを再起動する
|
|
13
|
+
runtime: typescript
|
|
14
|
+
entry: main.ts
|
|
15
|
+
handler: run
|
|
16
|
+
output_mode: raw
|
|
17
|
+
side_effect: true
|
|
18
|
+
|
|
19
|
+
invocation:
|
|
20
|
+
command: service.restart
|
|
21
|
+
phrases:
|
|
22
|
+
- Agent-Sinを再起動して
|
|
23
|
+
- agent-sinを再起動して
|
|
24
|
+
- 再起動して
|
|
25
|
+
- ボットを再起動して
|
|
26
|
+
phrases_i18n:
|
|
27
|
+
en:
|
|
28
|
+
- restart agent-sin
|
|
29
|
+
- restart the bot
|
|
30
|
+
- restart the service
|
|
31
|
+
ja:
|
|
32
|
+
- Agent-Sinを再起動して
|
|
33
|
+
- agent-sinを再起動して
|
|
34
|
+
- 再起動して
|
|
35
|
+
- ボットを再起動して
|
|
36
|
+
|
|
37
|
+
input:
|
|
38
|
+
schema:
|
|
39
|
+
type: object
|
|
40
|
+
additionalProperties: false
|
|
41
|
+
properties:
|
|
42
|
+
delay_ms:
|
|
43
|
+
type: integer
|
|
44
|
+
minimum: 12000
|
|
45
|
+
maximum: 30000
|
|
46
|
+
description: Restart delay in milliseconds
|
|
47
|
+
description_i18n:
|
|
48
|
+
en: Restart delay in milliseconds
|
|
49
|
+
ja: 再起動までの待ち時間(ミリ秒)
|
|
@@ -19,6 +19,7 @@ from datetime import datetime
|
|
|
19
19
|
|
|
20
20
|
sys.path.insert(0, os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "_shared"))
|
|
21
21
|
from i18n import localizer # noqa: E402
|
|
22
|
+
from _todo_lib import sync_todos, flush_todos # noqa: E402
|
|
22
23
|
|
|
23
24
|
|
|
24
25
|
async def run(ctx, input):
|
|
@@ -30,7 +31,7 @@ async def run(ctx, input):
|
|
|
30
31
|
if not text:
|
|
31
32
|
return result_skipped(loc.t("No ToDo", "ToDoなし"), loc.t("There is nothing to add.", "追加する内容がありません"))
|
|
32
33
|
|
|
33
|
-
items =
|
|
34
|
+
items = await sync_todos(ctx, input)
|
|
34
35
|
now = input.get("trigger", {}).get("time") or datetime.now().isoformat()
|
|
35
36
|
item = {
|
|
36
37
|
"id": secrets.token_hex(3),
|
|
@@ -42,6 +43,7 @@ async def run(ctx, input):
|
|
|
42
43
|
item["due"] = due
|
|
43
44
|
items.append(item)
|
|
44
45
|
await ctx.memory.set("items", items)
|
|
46
|
+
await flush_todos(ctx, input, items)
|
|
45
47
|
ctx.log.info(f"todo-add: id={item['id']} total={len(items)}")
|
|
46
48
|
|
|
47
49
|
summary = loc.t(f"Added: {text}", f"追加: {text}")
|
|
@@ -16,6 +16,7 @@ import sys
|
|
|
16
16
|
|
|
17
17
|
sys.path.insert(0, os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "_shared"))
|
|
18
18
|
from i18n import localizer # noqa: E402
|
|
19
|
+
from _todo_lib import sync_todos, flush_todos # noqa: E402
|
|
19
20
|
|
|
20
21
|
|
|
21
22
|
async def run(ctx, input):
|
|
@@ -26,7 +27,7 @@ async def run(ctx, input):
|
|
|
26
27
|
if not target_id:
|
|
27
28
|
return error_result(loc.t("ID is missing", "IDが指定されていません"), loc.t("Specify the ToDo ID to delete.", "削除するToDoのIDを指定してください"))
|
|
28
29
|
|
|
29
|
-
items =
|
|
30
|
+
items = await sync_todos(ctx, input)
|
|
30
31
|
if not items:
|
|
31
32
|
return error_result(loc.t("No ToDos", "ToDoがありません"), loc.t("There are no registered ToDos yet.", "ToDoがまだ登録されていません"))
|
|
32
33
|
|
|
@@ -42,6 +43,7 @@ async def run(ctx, input):
|
|
|
42
43
|
target = matches[0]
|
|
43
44
|
remaining = [i for i in items if i is not target]
|
|
44
45
|
await ctx.memory.set("items", remaining)
|
|
46
|
+
await flush_todos(ctx, input, remaining)
|
|
45
47
|
ctx.log.info(f"todo-delete: id={target.get('id')} remaining={len(remaining)}")
|
|
46
48
|
|
|
47
49
|
return {
|
|
@@ -16,6 +16,7 @@ from datetime import datetime
|
|
|
16
16
|
|
|
17
17
|
sys.path.insert(0, os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "_shared"))
|
|
18
18
|
from i18n import localizer # noqa: E402
|
|
19
|
+
from _todo_lib import sync_todos, flush_todos # noqa: E402
|
|
19
20
|
|
|
20
21
|
|
|
21
22
|
async def run(ctx, input):
|
|
@@ -26,7 +27,7 @@ async def run(ctx, input):
|
|
|
26
27
|
if not target_id:
|
|
27
28
|
return error_result(loc.t("ID is missing", "IDが指定されていません"), loc.t("Specify the ToDo ID to complete.", "完了するToDoのIDを指定してください"))
|
|
28
29
|
|
|
29
|
-
items =
|
|
30
|
+
items = await sync_todos(ctx, input)
|
|
30
31
|
if not items:
|
|
31
32
|
return error_result(loc.t("No ToDos", "ToDoがありません"), loc.t("There are no registered ToDos yet.", "ToDoがまだ登録されていません"))
|
|
32
33
|
|
|
@@ -48,6 +49,7 @@ async def run(ctx, input):
|
|
|
48
49
|
target["status"] = "done"
|
|
49
50
|
target["completed_at"] = now
|
|
50
51
|
await ctx.memory.set("items", items)
|
|
52
|
+
await flush_todos(ctx, input, items)
|
|
51
53
|
ctx.log.info(f"todo-done: id={target.get('id')}")
|
|
52
54
|
|
|
53
55
|
return result_ok(loc.t("Done", "完了"), loc.t(f"Done: {text}", f"完了: {text}"), target)
|
|
@@ -18,6 +18,7 @@ from datetime import datetime, timezone
|
|
|
18
18
|
|
|
19
19
|
sys.path.insert(0, os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "_shared"))
|
|
20
20
|
from i18n import localizer # noqa: E402
|
|
21
|
+
from _todo_lib import sync_todos # noqa: E402
|
|
21
22
|
|
|
22
23
|
|
|
23
24
|
async def run(ctx, input):
|
|
@@ -25,7 +26,9 @@ async def run(ctx, input):
|
|
|
25
26
|
args = input.get("args", {})
|
|
26
27
|
status = str(args.get("status", "open")).strip() or "open"
|
|
27
28
|
|
|
28
|
-
|
|
29
|
+
# todo-list は read-only スキル。Obsidian 側のマージ結果を表示するだけで、
|
|
30
|
+
# memory / ファイルへの永続化は行わない (次の write 可能スキルで反映される)。
|
|
31
|
+
items = await sync_todos(ctx, input)
|
|
29
32
|
open_items = [i for i in items if i.get("status") == "open"]
|
|
30
33
|
done_items = [i for i in items if i.get("status") == "done"]
|
|
31
34
|
if status == "open":
|
|
@@ -20,6 +20,7 @@ from datetime import datetime, timezone
|
|
|
20
20
|
|
|
21
21
|
sys.path.insert(0, os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "_shared"))
|
|
22
22
|
from i18n import localizer # noqa: E402
|
|
23
|
+
from _todo_lib import sync_todos, flush_todos # noqa: E402
|
|
23
24
|
|
|
24
25
|
|
|
25
26
|
async def run(ctx, input):
|
|
@@ -27,7 +28,7 @@ async def run(ctx, input):
|
|
|
27
28
|
args = input.get("args", {})
|
|
28
29
|
channel = str(args.get("channel", "auto")).strip() or "auto"
|
|
29
30
|
|
|
30
|
-
items =
|
|
31
|
+
items = await sync_todos(ctx, input)
|
|
31
32
|
if not items:
|
|
32
33
|
return result_ok(loc.t("Nothing due", "対象なし"), loc.t("There are no ToDos.", "ToDoはありません"), [], 0)
|
|
33
34
|
|
|
@@ -70,6 +71,7 @@ async def run(ctx, input):
|
|
|
70
71
|
|
|
71
72
|
if fired:
|
|
72
73
|
await ctx.memory.set("items", items)
|
|
74
|
+
await flush_todos(ctx, input, items)
|
|
73
75
|
|
|
74
76
|
pending = sum(
|
|
75
77
|
1 for i in items
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
"""Builtin: topic-knowledge-read
|
|
2
|
+
|
|
3
|
+
Read full per-topic knowledge files produced by nightly-topic-knowledge.
|
|
4
|
+
|
|
5
|
+
Read-only. The chat engine includes a short topic index on every turn; the AI
|
|
6
|
+
calls this skill to load the full content of the few topics it actually needs.
|
|
7
|
+
Missing / unsanitizable ids are reported in `data.missing` rather than failing
|
|
8
|
+
the whole call so the model can still use what it got.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
import json
|
|
14
|
+
import os
|
|
15
|
+
import re
|
|
16
|
+
import sys
|
|
17
|
+
from typing import Any
|
|
18
|
+
|
|
19
|
+
sys.path.insert(0, os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "_shared"))
|
|
20
|
+
from i18n import localizer # noqa: E402
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
TOPIC_ID_PATTERN = re.compile(r"^[a-z0-9]+(?:-[a-z0-9]+)*$")
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
async def run(ctx, input):
|
|
27
|
+
loc = localizer(input)
|
|
28
|
+
args = input.get("args", {}) or {}
|
|
29
|
+
sources = input.get("sources", {}) or {}
|
|
30
|
+
memory_dir = sources.get("memory_dir")
|
|
31
|
+
if not memory_dir:
|
|
32
|
+
return _err(
|
|
33
|
+
loc.t("Workspace unavailable", "ワークスペース不明"),
|
|
34
|
+
loc.t("memory_dir is not provided.", "memory_dir が取得できません"),
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
raw_ids = args.get("topic_ids")
|
|
38
|
+
if not isinstance(raw_ids, list) or not raw_ids:
|
|
39
|
+
return _err(
|
|
40
|
+
loc.t("Missing input", "入力不足"),
|
|
41
|
+
loc.t("topic_ids is required and must not be empty.", "topic_ids は必須かつ空にできません"),
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
topics_root = os.path.join(memory_dir, "topic-knowledge", "topics")
|
|
45
|
+
found: list[dict[str, Any]] = []
|
|
46
|
+
missing: list[str] = []
|
|
47
|
+
seen: set[str] = set()
|
|
48
|
+
for raw in raw_ids:
|
|
49
|
+
tid = _sanitize(raw)
|
|
50
|
+
if not tid or tid in seen:
|
|
51
|
+
if isinstance(raw, str) and raw.strip():
|
|
52
|
+
missing.append(raw.strip())
|
|
53
|
+
continue
|
|
54
|
+
seen.add(tid)
|
|
55
|
+
file_path = os.path.join(topics_root, f"{tid}.json")
|
|
56
|
+
if not os.path.isfile(file_path):
|
|
57
|
+
missing.append(tid)
|
|
58
|
+
continue
|
|
59
|
+
try:
|
|
60
|
+
with open(file_path, "r", encoding="utf-8") as f:
|
|
61
|
+
data = json.load(f)
|
|
62
|
+
except Exception as e:
|
|
63
|
+
ctx.log.warn(f"topic-knowledge-read: failed to read {tid}: {e}")
|
|
64
|
+
missing.append(tid)
|
|
65
|
+
continue
|
|
66
|
+
found.append({"topic_id": tid, "path": file_path, "data": data})
|
|
67
|
+
|
|
68
|
+
if not found:
|
|
69
|
+
return {
|
|
70
|
+
"status": "skipped",
|
|
71
|
+
"title": loc.t("No topics found", "該当なし"),
|
|
72
|
+
"summary": loc.t(
|
|
73
|
+
f"No knowledge files found for: {', '.join(missing) or '(none)'}",
|
|
74
|
+
f"該当するトピックナレッジが見つかりません: {', '.join(missing) or '(なし)'}",
|
|
75
|
+
),
|
|
76
|
+
"outputs": {},
|
|
77
|
+
"data": {"found": [], "missing": missing},
|
|
78
|
+
"suggestions": [],
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
ctx.log.info(
|
|
82
|
+
f"topic-knowledge-read: loaded {len(found)} topic(s), missing={len(missing)}"
|
|
83
|
+
)
|
|
84
|
+
missing_joined = ", ".join(missing)
|
|
85
|
+
en_suffix = f" (missing: {missing_joined})" if missing else ""
|
|
86
|
+
ja_suffix = f"(不在: {missing_joined})" if missing else ""
|
|
87
|
+
return {
|
|
88
|
+
"status": "ok",
|
|
89
|
+
"title": loc.t("Topics loaded", "読み込み完了"),
|
|
90
|
+
"summary": loc.t(
|
|
91
|
+
f"Loaded {len(found)} topic(s){en_suffix}",
|
|
92
|
+
f"{len(found)}件のトピックを読み込みました{ja_suffix}",
|
|
93
|
+
),
|
|
94
|
+
"outputs": {},
|
|
95
|
+
"data": {"found": found, "missing": missing},
|
|
96
|
+
"suggestions": [],
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def _sanitize(raw: object) -> str | None:
|
|
101
|
+
if not isinstance(raw, str):
|
|
102
|
+
return None
|
|
103
|
+
text = raw.strip().lower()
|
|
104
|
+
text = re.sub(r"[^a-z0-9]+", "-", text).strip("-")
|
|
105
|
+
if not text or not TOPIC_ID_PATTERN.match(text):
|
|
106
|
+
return None
|
|
107
|
+
return text
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def _err(title: str, summary: str) -> dict[str, Any]:
|
|
111
|
+
return {
|
|
112
|
+
"status": "error",
|
|
113
|
+
"title": title,
|
|
114
|
+
"summary": summary,
|
|
115
|
+
"outputs": {},
|
|
116
|
+
"data": {},
|
|
117
|
+
"suggestions": [],
|
|
118
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# Builtin: topic-knowledge-read
|
|
2
|
+
# Read one or more per-topic knowledge files generated by nightly-topic-knowledge.
|
|
3
|
+
# The chat engine surfaces a short topic index on every turn; the model uses this
|
|
4
|
+
# read skill to pull full details for the few topics that look relevant.
|
|
5
|
+
|
|
6
|
+
id: topic-knowledge-read
|
|
7
|
+
name: Topic Knowledge Read
|
|
8
|
+
name_i18n:
|
|
9
|
+
en: Topic Knowledge Read
|
|
10
|
+
ja: トピックナレッジ参照
|
|
11
|
+
description: Read full knowledge files for the given topic ids
|
|
12
|
+
description_i18n:
|
|
13
|
+
en: Read full knowledge files for the given topic ids
|
|
14
|
+
ja: 指定したトピックIDのナレッジ本文を読み出す
|
|
15
|
+
runtime: python
|
|
16
|
+
side_effect: false
|
|
17
|
+
|
|
18
|
+
invocation:
|
|
19
|
+
command: knowledge.read
|
|
20
|
+
phrases:
|
|
21
|
+
- read topic knowledge
|
|
22
|
+
- load topic details
|
|
23
|
+
phrases_i18n:
|
|
24
|
+
en:
|
|
25
|
+
- read topic knowledge
|
|
26
|
+
- load topic details
|
|
27
|
+
ja:
|
|
28
|
+
- トピックナレッジを読んで
|
|
29
|
+
- トピックの詳細を見せて
|
|
30
|
+
|
|
31
|
+
input:
|
|
32
|
+
schema:
|
|
33
|
+
type: object
|
|
34
|
+
additionalProperties: false
|
|
35
|
+
properties:
|
|
36
|
+
topic_ids:
|
|
37
|
+
type: array
|
|
38
|
+
minItems: 1
|
|
39
|
+
maxItems: 8
|
|
40
|
+
items:
|
|
41
|
+
type: string
|
|
42
|
+
minLength: 1
|
|
43
|
+
maxLength: 64
|
|
44
|
+
description: Topic ids to load. See `<topic-knowledge-index>` in the chat context for the available ids.
|
|
45
|
+
description_i18n:
|
|
46
|
+
en: Topic ids to load. See `<topic-knowledge-index>` in the chat context for the available ids.
|
|
47
|
+
ja: 読み込むトピックIDの配列。利用可能なIDは会話コンテキストの `<topic-knowledge-index>` を参照
|
|
48
|
+
required:
|
|
49
|
+
- topic_ids
|
|
@@ -10,9 +10,27 @@ export interface BuildModeActionDecision {
|
|
|
10
10
|
action: "exit" | "register" | "test" | "continue";
|
|
11
11
|
reason?: string;
|
|
12
12
|
}
|
|
13
|
+
export interface ScheduleRequestPayload {
|
|
14
|
+
id: string;
|
|
15
|
+
cron: string;
|
|
16
|
+
skill: string;
|
|
17
|
+
description?: string;
|
|
18
|
+
args?: Record<string, unknown>;
|
|
19
|
+
enabled?: boolean;
|
|
20
|
+
approve?: boolean;
|
|
21
|
+
}
|
|
22
|
+
export type ScheduleRequestDecision = {
|
|
23
|
+
matched: false;
|
|
24
|
+
reason?: string;
|
|
25
|
+
} | {
|
|
26
|
+
matched: true;
|
|
27
|
+
payload: ScheduleRequestPayload;
|
|
28
|
+
reason?: string;
|
|
29
|
+
};
|
|
13
30
|
interface ClassifyOptions {
|
|
14
31
|
modelId?: string;
|
|
15
32
|
}
|
|
16
33
|
export declare function classifyHandoffApproval(config: AppConfig, userText: string, history: ChatTurn[], pending: PendingHandoff, options?: ClassifyOptions): Promise<HandoffApprovalDecision>;
|
|
17
34
|
export declare function classifyBuildModeAction(config: AppConfig, userText: string, history: ChatTurn[], build: BuildModeState, options?: ClassifyOptions): Promise<BuildModeActionDecision>;
|
|
35
|
+
export declare function classifyScheduleRequest(config: AppConfig, userText: string, history: ChatTurn[], build: BuildModeState, options?: ClassifyOptions): Promise<ScheduleRequestDecision>;
|
|
18
36
|
export {};
|