agent-sin 0.1.11 → 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.
Files changed (97) hide show
  1. package/CHANGELOG.md +79 -0
  2. package/README.md +2 -1
  3. package/builtin-skills/_shared/_todo_lib.py +290 -0
  4. package/builtin-skills/even-g2-setup/main.ts +896 -0
  5. package/builtin-skills/even-g2-setup/skill.yaml +133 -0
  6. package/builtin-skills/memo-delete/main.py +28 -107
  7. package/builtin-skills/memo-delete/skill.yaml +10 -21
  8. package/builtin-skills/memo-index/main.py +96 -64
  9. package/builtin-skills/memo-index/skill.yaml +4 -10
  10. package/builtin-skills/memo-list/main.py +179 -0
  11. package/builtin-skills/memo-list/skill.yaml +51 -0
  12. package/builtin-skills/memo-save/main.py +191 -25
  13. package/builtin-skills/memo-save/skill.yaml +29 -5
  14. package/builtin-skills/memo-search/main.py +38 -18
  15. package/builtin-skills/memo-vector-search/main.py +11 -6
  16. package/builtin-skills/nightly-topic-knowledge/_feedback_lib.py +391 -0
  17. package/builtin-skills/nightly-topic-knowledge/_topics_lib.py +415 -0
  18. package/builtin-skills/nightly-topic-knowledge/main.py +403 -0
  19. package/builtin-skills/nightly-topic-knowledge/skill.yaml +88 -0
  20. package/builtin-skills/schedule-add/main.py +26 -0
  21. package/builtin-skills/service-restart/main.ts +249 -0
  22. package/builtin-skills/service-restart/skill.yaml +49 -0
  23. package/builtin-skills/todo-add/main.py +3 -1
  24. package/builtin-skills/todo-delete/main.py +3 -1
  25. package/builtin-skills/todo-done/main.py +3 -1
  26. package/builtin-skills/todo-list/main.py +4 -1
  27. package/builtin-skills/todo-tick/main.py +3 -1
  28. package/builtin-skills/topic-knowledge-read/main.py +118 -0
  29. package/builtin-skills/topic-knowledge-read/skill.yaml +49 -0
  30. package/dist/builder/build-action-classifier.d.ts +18 -0
  31. package/dist/builder/build-action-classifier.js +82 -1
  32. package/dist/builder/build-flow.d.ts +33 -4
  33. package/dist/builder/build-flow.js +251 -89
  34. package/dist/builder/builder-session.d.ts +1 -1
  35. package/dist/builder/builder-session.js +112 -7
  36. package/dist/builder/conversation-router.d.ts +4 -2
  37. package/dist/builder/conversation-router.js +19 -2
  38. package/dist/cli/index.js +323 -20
  39. package/dist/core/ai-provider.d.ts +1 -0
  40. package/dist/core/ai-provider.js +8 -3
  41. package/dist/core/chat-engine.d.ts +10 -3
  42. package/dist/core/chat-engine.js +1563 -197
  43. package/dist/core/config.d.ts +4 -0
  44. package/dist/core/config.js +82 -0
  45. package/dist/core/daily-memory-promotion.d.ts +7 -0
  46. package/dist/core/daily-memory-promotion.js +568 -14
  47. package/dist/core/image-attachments.d.ts +31 -0
  48. package/dist/core/image-attachments.js +237 -0
  49. package/dist/core/logger.d.ts +2 -1
  50. package/dist/core/logger.js +77 -1
  51. package/dist/core/memo-migration.d.ts +3 -0
  52. package/dist/core/memo-migration.js +422 -0
  53. package/dist/core/native-modules.d.ts +24 -0
  54. package/dist/core/native-modules.js +99 -0
  55. package/dist/core/notifier.d.ts +8 -3
  56. package/dist/core/notifier.js +191 -17
  57. package/dist/core/obsidian-vault.d.ts +19 -0
  58. package/dist/core/obsidian-vault.js +477 -0
  59. package/dist/core/operating-model.d.ts +2 -0
  60. package/dist/core/operating-model.js +15 -0
  61. package/dist/core/output-writer.d.ts +3 -2
  62. package/dist/core/output-writer.js +108 -7
  63. package/dist/core/profile-memory.js +22 -1
  64. package/dist/core/runtime.d.ts +2 -0
  65. package/dist/core/runtime.js +9 -1
  66. package/dist/core/secrets.d.ts +4 -0
  67. package/dist/core/secrets.js +34 -0
  68. package/dist/core/skill-history.d.ts +44 -0
  69. package/dist/core/skill-history.js +329 -0
  70. package/dist/core/skill-registry.d.ts +5 -0
  71. package/dist/core/skill-registry.js +11 -0
  72. package/dist/discord/bot.d.ts +13 -0
  73. package/dist/discord/bot.js +542 -10
  74. package/dist/even-g2/gateway.d.ts +15 -0
  75. package/dist/even-g2/gateway.js +868 -0
  76. package/dist/runtimes/codex-app-server.d.ts +5 -1
  77. package/dist/runtimes/codex-app-server.js +147 -8
  78. package/dist/runtimes/python-runner.js +82 -0
  79. package/dist/runtimes/typescript-runner.js +13 -1
  80. package/dist/skills-sdk/types.d.ts +19 -4
  81. package/dist/telegram/bot.d.ts +1 -0
  82. package/dist/telegram/bot.js +122 -31
  83. package/package.json +3 -1
  84. package/templates/even-g2-agent/README.md +83 -0
  85. package/templates/even-g2-agent/app.json +20 -0
  86. package/templates/even-g2-agent/index.html +31 -0
  87. package/templates/even-g2-agent/package-lock.json +1836 -0
  88. package/templates/even-g2-agent/package.json +22 -0
  89. package/templates/even-g2-agent/scripts/qr-auto.mjs +182 -0
  90. package/templates/even-g2-agent/src/embedded-config.ts +4 -0
  91. package/templates/even-g2-agent/src/main.ts +539 -0
  92. package/templates/even-g2-agent/src/style.css +70 -0
  93. package/templates/even-g2-agent/tsconfig.json +11 -0
  94. package/templates/skill-python/main.py +20 -2
  95. package/templates/skill-python/skill.yaml +9 -0
  96. package/templates/skill-typescript/main.ts +40 -5
  97. package/templates/skill-typescript/skill.yaml +9 -0
@@ -30,6 +30,10 @@ export interface CodexAppServerOptions {
30
30
  turnTimeoutMs?: number;
31
31
  onStderr?: (chunk: string) => void;
32
32
  }
33
+ export interface CodexTurnResult {
34
+ text: string;
35
+ generatedImagePaths: string[];
36
+ }
33
37
  export declare function getSharedCodexAppServer(model?: string): CodexAppServerSession;
34
38
  export declare function shutdownSharedCodexAppServer(): Promise<void>;
35
39
  export declare class CodexAppServerSession {
@@ -42,7 +46,7 @@ export declare class CodexAppServerSession {
42
46
  private readonly options;
43
47
  private exitReason;
44
48
  constructor(options?: CodexAppServerOptions);
45
- sendTurn(text: string, options?: CodexTurnOptions): Promise<string>;
49
+ sendTurn(text: string, options?: CodexTurnOptions): Promise<CodexTurnResult>;
46
50
  stop(): Promise<void>;
47
51
  isRunning(): boolean;
48
52
  private ensureStarted;
@@ -1,7 +1,13 @@
1
1
  import { spawn } from "node:child_process";
2
+ import crypto from "node:crypto";
3
+ import { mkdir, stat, writeFile } from "node:fs/promises";
4
+ import os from "node:os";
5
+ import path from "node:path";
6
+ import { detectImageMimeTypeFromBuffer, imageExtensionForMimeType, } from "../core/image-attachments.js";
2
7
  import { l } from "../core/i18n.js";
3
8
  const DEFAULT_STARTUP_TIMEOUT_MS = 30_000;
4
9
  const DEFAULT_TURN_TIMEOUT_MS = 5 * 60_000;
10
+ const MAX_GENERATED_IMAGE_BYTES = 20 * 1024 * 1024;
5
11
  function snippet(value) {
6
12
  if (typeof value !== "string") {
7
13
  return undefined;
@@ -40,8 +46,8 @@ export class CodexAppServerSession {
40
46
  constructor(options = {}) {
41
47
  const baseArgs = options.args ? [...options.args] : ["app-server"];
42
48
  const model = options.model || process.env.AGENT_SIN_CODEX_MODEL;
43
- if (model && !baseArgs.includes("--model") && !baseArgs.includes("-m")) {
44
- baseArgs.push("--model", model);
49
+ if (model && !hasConfigOverride(baseArgs, "model")) {
50
+ baseArgs.push("-c", `model=${JSON.stringify(model)}`);
45
51
  }
46
52
  this.options = {
47
53
  bin: options.bin || process.env.AGENT_SIN_CODEX_BIN || "codex",
@@ -211,6 +217,9 @@ export class CodexAppServerSession {
211
217
  return new Promise((resolve, reject) => {
212
218
  let assistantText = "";
213
219
  let settled = false;
220
+ const generatedImagePaths = [];
221
+ const generatedImageKeys = new Set();
222
+ const generatedImageTasks = [];
214
223
  const emit = (event) => {
215
224
  if (!options.onProgress) {
216
225
  return;
@@ -259,6 +268,17 @@ export class CodexAppServerSession {
259
268
  else if (item && (item.type === "command" || item.type === "tool_call")) {
260
269
  emit({ kind: "tool", name: item.name || item.type, text: l("done", "完了") });
261
270
  }
271
+ else if (item && item.type === "imageGeneration") {
272
+ generatedImageTasks.push(captureGeneratedImage(item, options.cwd, generatedImagePaths, generatedImageKeys));
273
+ emit({ kind: "tool", name: "image_generation", text: l("done", "完了") });
274
+ }
275
+ }
276
+ else if (method === "rawResponseItem/completed") {
277
+ const item = params.item;
278
+ if (item && item.type === "image_generation_call") {
279
+ generatedImageTasks.push(captureGeneratedImage(item, options.cwd, generatedImagePaths, generatedImageKeys));
280
+ emit({ kind: "tool", name: "image_generation", text: l("done", "完了") });
281
+ }
262
282
  }
263
283
  else if (method === "turn/completed") {
264
284
  const turn = params.turn;
@@ -270,6 +290,11 @@ export class CodexAppServerSession {
270
290
  if (fromTurn) {
271
291
  assistantText = fromTurn;
272
292
  }
293
+ for (const entry of turn.items) {
294
+ if (entry && entry.type === "imageGeneration") {
295
+ generatedImageTasks.push(captureGeneratedImage(entry, options.cwd, generatedImagePaths, generatedImageKeys));
296
+ }
297
+ }
273
298
  }
274
299
  finish(true, undefined);
275
300
  }
@@ -285,12 +310,23 @@ export class CodexAppServerSession {
285
310
  settled = true;
286
311
  clearTimeout(timeout);
287
312
  this.notificationHandlers.delete(handler);
288
- if (ok) {
289
- resolve(assistantText);
290
- }
291
- else {
292
- reject(error || new Error("codex app-server: unknown failure"));
293
- }
313
+ Promise.all(generatedImageTasks)
314
+ .then(() => {
315
+ if (ok) {
316
+ resolve({ text: assistantText, generatedImagePaths });
317
+ }
318
+ else {
319
+ reject(error || new Error("codex app-server: unknown failure"));
320
+ }
321
+ })
322
+ .catch((imageError) => {
323
+ if (ok) {
324
+ resolve({ text: assistantText, generatedImagePaths });
325
+ }
326
+ else {
327
+ reject(error || imageError);
328
+ }
329
+ });
294
330
  };
295
331
  const timeout = setTimeout(() => {
296
332
  finish(false, new Error(`codex app-server: turn timed out after ${this.options.turnTimeoutMs}ms`));
@@ -306,3 +342,106 @@ export class CodexAppServerSession {
306
342
  });
307
343
  }
308
344
  }
345
+ function hasConfigOverride(args, key) {
346
+ for (let index = 0; index < args.length; index += 1) {
347
+ const arg = args[index];
348
+ if ((arg === "-c" || arg === "--config") && args[index + 1]?.startsWith(`${key}=`)) {
349
+ return true;
350
+ }
351
+ if (arg.startsWith("-c") && arg.slice(2).trimStart().startsWith(`${key}=`)) {
352
+ return true;
353
+ }
354
+ if (arg.startsWith("--config=") && arg.slice("--config=".length).startsWith(`${key}=`)) {
355
+ return true;
356
+ }
357
+ }
358
+ return false;
359
+ }
360
+ async function captureGeneratedImage(item, cwd, paths, keys) {
361
+ if (!item || typeof item !== "object")
362
+ return;
363
+ const record = item;
364
+ const id = typeof record.id === "string" ? record.id : undefined;
365
+ const savedPath = typeof record.savedPath === "string" ? record.savedPath : undefined;
366
+ const result = typeof record.result === "string" ? record.result : undefined;
367
+ const key = id || (result ? imageResultKey(result) : savedPath);
368
+ if (key && keys.has(key))
369
+ return;
370
+ if (key)
371
+ keys.add(key);
372
+ if (savedPath) {
373
+ const usable = await existingImagePath(savedPath);
374
+ if (usable && !paths.includes(usable)) {
375
+ paths.push(usable);
376
+ return;
377
+ }
378
+ }
379
+ if (!result)
380
+ return;
381
+ const resultPath = await persistImageGenerationResult(result, cwd, id);
382
+ if (resultPath && !paths.includes(resultPath)) {
383
+ paths.push(resultPath);
384
+ }
385
+ }
386
+ async function existingImagePath(filePath) {
387
+ try {
388
+ const info = await stat(filePath);
389
+ return info.isFile() && info.size > 0 ? filePath : null;
390
+ }
391
+ catch {
392
+ return null;
393
+ }
394
+ }
395
+ async function persistImageGenerationResult(value, cwd, id) {
396
+ const parsed = parseImageGenerationResult(value);
397
+ if (!parsed || parsed.buffer.length === 0 || parsed.buffer.length > MAX_GENERATED_IMAGE_BYTES) {
398
+ return null;
399
+ }
400
+ const mimeType = parsed.mimeType || detectImageMimeTypeFromBuffer(parsed.buffer);
401
+ const ext = imageExtensionForMimeType(mimeType);
402
+ const safeId = (id || "image").replace(/[^a-zA-Z0-9_-]/g, "").slice(0, 40) || "image";
403
+ const hash = crypto.createHash("sha256").update(parsed.buffer).digest("hex").slice(0, 10);
404
+ const filename = `${new Date().toISOString().replace(/[:.]/g, "-")}-${safeId}-${hash}${ext}`;
405
+ for (const dir of candidateImageDirs(cwd)) {
406
+ try {
407
+ await mkdir(dir, { recursive: true });
408
+ const filePath = path.join(dir, filename);
409
+ await writeFile(filePath, parsed.buffer);
410
+ return filePath;
411
+ }
412
+ catch {
413
+ // Try the fallback directory.
414
+ }
415
+ }
416
+ return null;
417
+ }
418
+ function parseImageGenerationResult(value) {
419
+ const trimmed = value.trim();
420
+ const dataUrl = trimmed.match(/^data:(image\/[a-z0-9.+-]+);base64,([\s\S]+)$/i);
421
+ const mimeType = dataUrl?.[1];
422
+ const rawBase64 = dataUrl ? dataUrl[2] : trimmed;
423
+ if (rawBase64.length === 0 || rawBase64.length > Math.ceil(MAX_GENERATED_IMAGE_BYTES * 1.4)) {
424
+ return null;
425
+ }
426
+ if (!/^[A-Za-z0-9+/=\s_-]+$/.test(rawBase64)) {
427
+ return null;
428
+ }
429
+ const normalized = rawBase64.replace(/[\s_-]/g, (char) => (char === "-" ? "+" : char === "_" ? "/" : ""));
430
+ try {
431
+ return { buffer: Buffer.from(normalized, "base64"), mimeType };
432
+ }
433
+ catch {
434
+ return null;
435
+ }
436
+ }
437
+ function candidateImageDirs(cwd) {
438
+ const dirs = [];
439
+ if (cwd) {
440
+ dirs.push(path.join(cwd, "codex-images"));
441
+ }
442
+ dirs.push(path.join(os.tmpdir(), "agent-sin-codex-images"));
443
+ return dirs;
444
+ }
445
+ function imageResultKey(value) {
446
+ return crypto.createHash("sha256").update(value).digest("hex");
447
+ }
@@ -4,10 +4,13 @@ import path from "node:path";
4
4
  import { resolveSkillEntryPath } from "../core/skill-registry.js";
5
5
  import { getAiProvider } from "../core/ai-provider.js";
6
6
  import { notify as runNotify } from "../core/notifier.js";
7
+ import { appendSkillHistory, listSkillHistory, readSkillHistoryRaw, } from "../core/skill-history.js";
7
8
  const AI_REQUEST_PREFIX = "AGENT_SIN_AI_REQUEST::";
8
9
  const AI_RESPONSE_PREFIX = "AGENT_SIN_AI_RESPONSE::";
9
10
  const NOTIFY_REQUEST_PREFIX = "AGENT_SIN_NOTIFY_REQUEST::";
10
11
  const NOTIFY_RESPONSE_PREFIX = "AGENT_SIN_NOTIFY_RESPONSE::";
12
+ const HISTORY_REQUEST_PREFIX = "AGENT_SIN_HISTORY_REQUEST::";
13
+ const HISTORY_RESPONSE_PREFIX = "AGENT_SIN_HISTORY_RESPONSE::";
11
14
  const CTX_LOG_PATTERN = /^\[(info|warn|error)\]\s+([\s\S]*)$/;
12
15
  export function candidatePythonInterpreters(config, platform = process.platform) {
13
16
  const venvDir = path.join(config.workspace, ".venv");
@@ -83,6 +86,13 @@ export async function runPythonSkill(config, manifest, input) {
83
86
  });
84
87
  return;
85
88
  }
89
+ if (line.startsWith(HISTORY_REQUEST_PREFIX)) {
90
+ const payloadJson = line.slice(HISTORY_REQUEST_PREFIX.length);
91
+ handleHistoryRequest(payloadJson).catch((error) => {
92
+ stderrLines.push(`[history-error] ${error instanceof Error ? error.message : String(error)}`);
93
+ });
94
+ return;
95
+ }
86
96
  const ctxMatch = line.match(CTX_LOG_PATTERN);
87
97
  if (ctxMatch) {
88
98
  ctxLogs.push({ level: ctxMatch[1], message: ctxMatch[2] });
@@ -103,6 +113,7 @@ export async function runPythonSkill(config, manifest, input) {
103
113
  const response = await getAiProvider()(config, {
104
114
  model_id: request.model_id,
105
115
  messages: request.messages,
116
+ cwd: config.workspace,
106
117
  });
107
118
  child.stdin.write(`${AI_RESPONSE_PREFIX}${JSON.stringify({ id: request.id, ok: true, response })}\n`);
108
119
  }
@@ -114,6 +125,40 @@ export async function runPythonSkill(config, manifest, input) {
114
125
  })}\n`);
115
126
  }
116
127
  }
128
+ async function handleHistoryRequest(payloadJson) {
129
+ let request;
130
+ try {
131
+ request = JSON.parse(payloadJson);
132
+ }
133
+ catch (error) {
134
+ child.stdin.write(`${HISTORY_RESPONSE_PREFIX}${JSON.stringify({ id: "?", ok: false, error: `Invalid history request JSON: ${String(error)}` })}\n`);
135
+ return;
136
+ }
137
+ try {
138
+ const payload = request.payload || {};
139
+ let data = null;
140
+ if (request.op === "append") {
141
+ data = await appendSkillHistory(config, manifest, payload);
142
+ }
143
+ else if (request.op === "list") {
144
+ data = await listSkillHistory(config, manifest, payload);
145
+ }
146
+ else if (request.op === "read") {
147
+ data = await readSkillHistoryRaw(config, manifest, payload);
148
+ }
149
+ else {
150
+ throw new Error(`Unknown history op: ${request.op}`);
151
+ }
152
+ child.stdin.write(`${HISTORY_RESPONSE_PREFIX}${JSON.stringify({ id: request.id, ok: true, data })}\n`);
153
+ }
154
+ catch (error) {
155
+ child.stdin.write(`${HISTORY_RESPONSE_PREFIX}${JSON.stringify({
156
+ id: request.id,
157
+ ok: false,
158
+ error: error instanceof Error ? error.message : String(error),
159
+ })}\n`);
160
+ }
161
+ }
117
162
  async function handleNotifyRequest(payloadJson) {
118
163
  let request;
119
164
  try {
@@ -134,6 +179,15 @@ export async function runPythonSkill(config, manifest, input) {
134
179
  to: typeof args.to === "string" ? args.to : undefined,
135
180
  discordThreadId: typeof args.discordThreadId === "string" ? args.discordThreadId : undefined,
136
181
  telegramThreadId: typeof args.telegramThreadId === "string" ? args.telegramThreadId : undefined,
182
+ filePath: typeof args.filePath === "string" ? args.filePath : undefined,
183
+ filePaths: Array.isArray(args.filePaths)
184
+ ? args.filePaths.filter((item) => typeof item === "string")
185
+ : undefined,
186
+ imagePath: typeof args.imagePath === "string" ? args.imagePath : undefined,
187
+ imagePaths: Array.isArray(args.imagePaths)
188
+ ? args.imagePaths.filter((item) => typeof item === "string")
189
+ : undefined,
190
+ cwd: config.workspace,
137
191
  });
138
192
  child.stdin.write(`${NOTIFY_RESPONSE_PREFIX}${JSON.stringify({ id: request.id, ok: result.ok, channel: result.channel, detail: result.detail })}\n`);
139
193
  }
@@ -238,6 +292,7 @@ class AI:
238
292
  "model_id": response.get("model_id"),
239
293
  "provider": response.get("provider"),
240
294
  "text": response.get("text", ""),
295
+ "generated_images": response.get("generated_images") or [],
241
296
  }
242
297
 
243
298
  class Memory:
@@ -275,11 +330,38 @@ async def notify_call(args):
275
330
  "detail": envelope.get("detail"),
276
331
  }
277
332
 
333
+ async def history_call(op, payload):
334
+ request_id = str(uuid.uuid4())
335
+ request = {"id": request_id, "op": op, "payload": payload or {}}
336
+ print("AGENT_SIN_HISTORY_REQUEST::" + json.dumps(request, ensure_ascii=False, default=str), file=sys.stderr, flush=True)
337
+ line = sys.stdin.readline()
338
+ if not line:
339
+ raise RuntimeError("History channel closed")
340
+ marker = "AGENT_SIN_HISTORY_RESPONSE::"
341
+ idx = line.find(marker)
342
+ if idx < 0:
343
+ raise RuntimeError(f"Unexpected history response: {line.strip()}")
344
+ envelope = json.loads(line[idx + len(marker):])
345
+ if not envelope.get("ok"):
346
+ raise RuntimeError(envelope.get("error") or "History error")
347
+ return envelope.get("data")
348
+
349
+ class History:
350
+ async def append(self, args):
351
+ if not isinstance(args, dict):
352
+ raise RuntimeError("ctx.history.append requires a dict argument")
353
+ return await history_call("append", args)
354
+ async def list(self, options=None):
355
+ return await history_call("list", options or {})
356
+ async def read(self, options=None):
357
+ return await history_call("read", options or {})
358
+
278
359
  class Ctx:
279
360
  def __init__(self):
280
361
  self.log = Log()
281
362
  self.ai = AI()
282
363
  self.memory = Memory()
364
+ self.history = History()
283
365
  def now(self):
284
366
  return datetime.now(timezone.utc).isoformat()
285
367
  async def notify(self, args):
@@ -7,6 +7,7 @@ import ts from "typescript";
7
7
  import { resolveSkillEntryPath } from "../core/skill-registry.js";
8
8
  import { getAiProvider } from "../core/ai-provider.js";
9
9
  import { notify as runNotify } from "../core/notifier.js";
10
+ import { appendSkillHistory, listSkillHistory, readSkillHistoryRaw, } from "../core/skill-history.js";
10
11
  export async function runTypeScriptSkill(config, manifest, input) {
11
12
  const entry = await resolveSkillEntryPath(manifest);
12
13
  const skillDir = await realpath(manifest.dir);
@@ -113,13 +114,14 @@ function createContext(config, manifest, initialMemory) {
113
114
  { role: "system", content: `You are an AI step "${step.id}" of an Agent-Sin skill. Purpose: ${step.purpose}` },
114
115
  { role: "user", content: typeof payload === "string" ? payload : JSON.stringify(payload) },
115
116
  ];
116
- const response = await getAiProvider()(config, { model_id: modelId, messages });
117
+ const response = await getAiProvider()(config, { model_id: modelId, messages, cwd: config.workspace });
117
118
  return {
118
119
  status: "ok",
119
120
  step_id: stepId,
120
121
  model_id: modelId,
121
122
  provider: response.provider,
122
123
  text: response.text,
124
+ generated_images: response.generated_images || [],
123
125
  };
124
126
  }
125
127
  catch (error) {
@@ -145,6 +147,11 @@ function createContext(config, manifest, initialMemory) {
145
147
  to: args.to,
146
148
  discordThreadId: args.discordThreadId,
147
149
  telegramThreadId: args.telegramThreadId,
150
+ filePath: args.filePath,
151
+ filePaths: args.filePaths,
152
+ imagePath: args.imagePath,
153
+ imagePaths: args.imagePaths,
154
+ cwd: config.workspace,
148
155
  });
149
156
  return { ok: result.ok, channel: result.channel, detail: result.detail };
150
157
  },
@@ -164,6 +171,11 @@ function createContext(config, manifest, initialMemory) {
164
171
  return true;
165
172
  },
166
173
  },
174
+ history: {
175
+ append: async (args) => appendSkillHistory(config, manifest, args),
176
+ list: async (options = {}) => listSkillHistory(config, manifest, options),
177
+ read: async (options = {}) => readSkillHistoryRaw(config, manifest, options),
178
+ },
167
179
  now: () => new Date().toISOString(),
168
180
  },
169
181
  memoryUpdates,
@@ -1,3 +1,14 @@
1
+ export interface SkillInputSources {
2
+ workspace?: string;
3
+ notes_dir?: string;
4
+ memory_dir?: string;
5
+ index_dir?: string;
6
+ logs_dir?: string;
7
+ skill_output_dir?: string;
8
+ skillOutputDir?: string;
9
+ locale?: string;
10
+ [key: string]: unknown;
11
+ }
1
12
  export interface SkillInput {
2
13
  args: Record<string, unknown>;
3
14
  trigger: {
@@ -5,19 +16,23 @@ export interface SkillInput {
5
16
  id: string;
6
17
  time: string;
7
18
  };
8
- sources: Record<string, unknown>;
19
+ sources: SkillInputSources;
9
20
  memory: Record<string, unknown>;
10
21
  }
11
- export type NotifyChannel = "auto" | "macos" | "discord" | "telegram" | "slack" | "mail" | "stderr";
22
+ export type NotifyChannel = "auto" | "macos" | "discord" | "telegram" | "slack" | "mail" | "g2" | "stderr";
12
23
  export interface NotifyArgs {
13
- title: string;
14
- body: string;
24
+ title?: string;
25
+ body?: string;
15
26
  subtitle?: string;
16
27
  sound?: boolean;
17
28
  channel?: NotifyChannel;
18
29
  to?: string;
19
30
  discordThreadId?: string;
20
31
  telegramThreadId?: string;
32
+ filePath?: string;
33
+ filePaths?: string[];
34
+ imagePath?: string;
35
+ imagePaths?: string[];
21
36
  }
22
37
  export interface NotifyOutcome {
23
38
  ok: boolean;
@@ -90,5 +90,6 @@ export declare function shouldUseTelegramDraftStream(message: TelegramMessage):
90
90
  export declare function telegramSendPayload(chatId: string, content: string, options?: TelegramSendOptions): Record<string, unknown>;
91
91
  export declare function telegramDraftPayload(message: TelegramMessage, draftId: number, text: string): Record<string, unknown>;
92
92
  export declare function formatTelegramDraftProgress(event: AiProgressEvent): string | null;
93
+ export declare function formatTelegramChatDraftProgress(event: AiProgressEvent): string | null;
93
94
  export declare function loadTelegramHistories(filePath: string): Promise<Map<string, ChatTurn[]>>;
94
95
  export declare function loadTelegramIntentRuntimes(filePath: string): Promise<Map<string, IntentRuntime>>;