@vellumai/assistant 0.6.0 → 0.6.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/AGENTS.md +4 -0
- package/ARCHITECTURE.md +68 -15
- package/Dockerfile +2 -2
- package/bun.lock +6 -2
- package/docker-entrypoint.sh +32 -1
- package/docs/architecture/integrations.md +1 -1
- package/docs/architecture/memory.md +21 -24
- package/openapi.yaml +538 -3
- package/package.json +5 -1
- package/src/__tests__/anthropic-provider.test.ts +160 -95
- package/src/__tests__/app-dir-path-guard.test.ts +1 -0
- package/src/__tests__/app-executors.test.ts +47 -1
- package/src/__tests__/app-source-watcher.test.ts +159 -0
- package/src/__tests__/checker.test.ts +38 -6
- package/src/__tests__/config-schema.test.ts +5 -0
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +4 -6
- package/src/__tests__/conversation-agent-loop.test.ts +4 -51
- package/src/__tests__/conversation-history-web-search.test.ts +1 -1
- package/src/__tests__/conversation-runtime-assembly.test.ts +653 -832
- package/src/__tests__/conversation-runtime-workspace.test.ts +1 -93
- package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +17 -4
- package/src/__tests__/conversation-wipe.test.ts +2 -6
- package/src/__tests__/conversation-workspace-cache-state.test.ts +6 -12
- package/src/__tests__/conversation-workspace-injection.test.ts +25 -26
- package/src/__tests__/conversation-workspace-tool-tracking.test.ts +1 -1
- package/src/__tests__/copy-composer-tc-templates.test.ts +335 -0
- package/src/__tests__/date-context.test.ts +76 -210
- package/src/__tests__/db-schedule-syntax-migration.test.ts +16 -1
- package/src/__tests__/file-list-tool.test.ts +219 -0
- package/src/__tests__/first-greeting.test.ts +1 -1
- package/src/__tests__/heartbeat-service.test.ts +180 -3
- package/src/__tests__/identity-routes.test.ts +328 -0
- package/src/__tests__/injection-block.test.ts +24 -0
- package/src/__tests__/install-skill-routing.test.ts +7 -6
- package/src/__tests__/jobs-store-qdrant-breaker.test.ts +15 -14
- package/src/__tests__/list-messages-tool-merge.test.ts +300 -0
- package/src/__tests__/llm-context-normalization.test.ts +18 -18
- package/src/__tests__/llm-context-route-provider.test.ts +101 -0
- package/src/__tests__/llm-request-log-turn-query.test.ts +162 -0
- package/src/__tests__/log-export-workspace.test.ts +72 -105
- package/src/__tests__/mcp-abort-signal.test.ts +5 -0
- package/src/__tests__/mcp-client-auth.test.ts +5 -0
- package/src/__tests__/memory-recall-log-store.test.ts +132 -0
- package/src/__tests__/migration-export-streaming.test.ts +304 -0
- package/src/__tests__/migration-import-commit-http.test.ts +11 -10
- package/src/__tests__/mock-fetch.ts +87 -0
- package/src/__tests__/notification-decision-recipient-context.test.ts +282 -0
- package/src/__tests__/onboarding-template-contract.test.ts +62 -14
- package/src/__tests__/parser.test.ts +32 -0
- package/src/__tests__/permission-checker-host-gate.test.ts +452 -0
- package/src/__tests__/permission-controls-v2-flag.test.ts +55 -0
- package/src/__tests__/permission-mode-sse.test.ts +418 -0
- package/src/__tests__/permission-mode-store.test.ts +277 -0
- package/src/__tests__/permission-mode.test.ts +101 -0
- package/src/__tests__/platform-bash-auto-approve.test.ts +359 -0
- package/src/__tests__/profiler-routes.test.ts +502 -0
- package/src/__tests__/profiler-run-store.test.ts +441 -0
- package/src/__tests__/proxy-approval-callback.test.ts +4 -75
- package/src/__tests__/registry.test.ts +1 -1
- package/src/__tests__/sandbox-host-parity.test.ts +5 -4
- package/src/__tests__/scheduler-reuse-conversation.test.ts +368 -0
- package/src/__tests__/scrub-corrupted-image-attachments.test.ts +278 -0
- package/src/__tests__/search-skills-unified.test.ts +4 -3
- package/src/__tests__/send-endpoint-busy.test.ts +42 -3
- package/src/__tests__/set-permission-mode.test.ts +274 -0
- package/src/__tests__/skill-load-feature-flag.test.ts +12 -0
- package/src/__tests__/skill-memory.test.ts +2 -783
- package/src/__tests__/strip-memory-injections.test.ts +187 -0
- package/src/__tests__/subagent-detail.test.ts +84 -0
- package/src/__tests__/subagent-disposal.test.ts +308 -0
- package/src/__tests__/subagent-manager-notify.test.ts +19 -10
- package/src/__tests__/subagent-notify-parent.test.ts +390 -0
- package/src/__tests__/subagent-role-registry.test.ts +108 -0
- package/src/__tests__/subagent-tool-filtering.test.ts +71 -0
- package/src/__tests__/subagent-tools.test.ts +464 -4
- package/src/__tests__/system-prompt-ask-mode.test.ts +139 -0
- package/src/__tests__/task-memory-cleanup.test.ts +12 -12
- package/src/__tests__/terminal-tools.test.ts +17 -27
- package/src/__tests__/test-preload.ts +4 -0
- package/src/__tests__/tool-executor.test.ts +4 -26
- package/src/__tests__/tool-side-effects-slack-dm.test.ts +1 -0
- package/src/__tests__/top-level-renderer.test.ts +10 -13
- package/src/__tests__/trusted-contact-lifecycle-notifications.test.ts +116 -2
- package/src/__tests__/workspace-migration-028-recover-conversations-from-disk-view.test.ts +387 -0
- package/src/agent/loop.ts +6 -0
- package/src/approvals/guardian-request-resolvers.ts +24 -0
- package/src/avatar/traits-png-sync.ts +3 -3
- package/src/cli/__tests__/run-assistant-command.ts +29 -0
- package/src/cli/commands/__tests__/email-download.test.ts +245 -0
- package/src/cli/commands/__tests__/email-list.test.ts +192 -0
- package/src/cli/commands/__tests__/email-register.test.ts +186 -0
- package/src/cli/commands/__tests__/email-send.test.ts +291 -0
- package/src/cli/commands/__tests__/email-status.test.ts +181 -0
- package/src/cli/commands/__tests__/email-unregister.test.ts +139 -0
- package/src/cli/commands/__tests__/routes.test.ts +562 -0
- package/src/cli/commands/conversations.ts +1 -8
- package/src/cli/commands/email.ts +584 -835
- package/src/cli/commands/memory.ts +1 -34
- package/src/cli/commands/notifications.ts +7 -2
- package/src/cli/commands/oauth/connect.ts +14 -5
- package/src/cli/commands/routes.ts +396 -0
- package/src/cli/commands/skills.ts +130 -20
- package/src/cli/program.ts +2 -0
- package/src/cli.ts +1 -120
- package/src/config/bundled-skills/app-builder/SKILL.md +4 -1
- package/src/config/bundled-skills/gmail/SKILL.md +2 -2
- package/src/config/bundled-skills/messaging/SKILL.md +7 -0
- package/src/config/bundled-skills/schedule/SKILL.md +22 -2
- package/src/config/bundled-skills/schedule/TOOLS.json +8 -0
- package/src/config/bundled-skills/settings/tools/avatar-get.ts +3 -13
- package/src/config/bundled-skills/settings/tools/avatar-remove.ts +2 -4
- package/src/config/bundled-skills/settings/tools/avatar-update.ts +5 -2
- package/src/config/bundled-skills/slack/SKILL.md +2 -0
- package/src/config/bundled-skills/subagent/SKILL.md +43 -3
- package/src/config/bundled-skills/subagent/TOOLS.json +29 -4
- package/src/config/env-registry.ts +63 -0
- package/src/config/feature-flag-registry.json +17 -1
- package/src/config/schema.ts +8 -0
- package/src/config/schemas/filing.ts +51 -0
- package/src/config/schemas/heartbeat.ts +15 -12
- package/src/config/schemas/memory-lifecycle.ts +12 -0
- package/src/config/schemas/security.ts +14 -0
- package/src/daemon/app-source-watcher.ts +93 -0
- package/src/daemon/config-watcher.ts +79 -1
- package/src/daemon/conversation-agent-loop-handlers.ts +20 -0
- package/src/daemon/conversation-agent-loop.ts +158 -65
- package/src/daemon/conversation-history.ts +4 -19
- package/src/daemon/conversation-lifecycle.ts +8 -14
- package/src/daemon/conversation-process.ts +13 -7
- package/src/daemon/conversation-runtime-assembly.ts +300 -306
- package/src/daemon/conversation-tool-setup.ts +44 -14
- package/src/daemon/conversation-workspace.ts +1 -2
- package/src/daemon/conversation.ts +18 -0
- package/src/daemon/date-context.ts +26 -53
- package/src/daemon/first-greeting.ts +1 -1
- package/src/daemon/handlers/conversations.ts +4 -7
- package/src/daemon/handlers/shared.test.ts +143 -0
- package/src/daemon/handlers/shared.ts +63 -5
- package/src/daemon/handlers/skills.ts +11 -18
- package/src/daemon/lifecycle.ts +199 -157
- package/src/daemon/message-types/conversations.ts +25 -6
- package/src/daemon/message-types/messages.ts +9 -1
- package/src/daemon/message-types/schedules.ts +1 -0
- package/src/daemon/message-types/settings.ts +6 -0
- package/src/daemon/profiler-run-store.ts +557 -0
- package/src/daemon/server.ts +89 -9
- package/src/daemon/shutdown-handlers.ts +5 -0
- package/src/daemon/tool-side-effects.ts +23 -3
- package/src/export/transcript-formatter.ts +148 -0
- package/src/filing/filing-service.ts +228 -0
- package/src/heartbeat/heartbeat-service.ts +96 -7
- package/src/mcp/client.ts +6 -0
- package/src/mcp/mcp-oauth-provider.ts +149 -27
- package/src/memory/admin.ts +33 -32
- package/src/memory/app-store.ts +69 -0
- package/src/memory/conversation-bootstrap.ts +1 -1
- package/src/memory/conversation-crud.ts +136 -107
- package/src/memory/conversation-group-migration.ts +1 -1
- package/src/memory/conversation-queries.ts +58 -12
- package/src/memory/conversation-title-service.ts +1 -0
- package/src/memory/db-init.ts +182 -376
- package/src/memory/graph/bootstrap.ts +75 -66
- package/src/memory/graph/capability-seed.ts +167 -15
- package/src/memory/graph/consolidation.ts +38 -4
- package/src/memory/graph/conversation-graph-memory.ts +133 -104
- package/src/memory/graph/extraction-job.ts +9 -4
- package/src/memory/graph/extraction.ts +66 -23
- package/src/memory/graph/graph-memory-state-store.ts +37 -0
- package/src/memory/graph/graph-search.ts +29 -15
- package/src/memory/graph/injection.ts +38 -8
- package/src/memory/graph/inspect.ts +12 -3
- package/src/memory/graph/retriever.ts +365 -262
- package/src/memory/graph/store.test.ts +48 -0
- package/src/memory/graph/store.ts +150 -11
- package/src/memory/graph/tool-handlers.ts +84 -209
- package/src/memory/graph/tools.ts +8 -52
- package/src/memory/graph/types.ts +24 -0
- package/src/memory/job-handlers/cleanup.ts +44 -1
- package/src/memory/jobs-store.ts +70 -60
- package/src/memory/jobs-worker.ts +44 -28
- package/src/memory/llm-request-log-store.ts +96 -12
- package/src/memory/memory-recall-log-store.ts +49 -5
- package/src/memory/migrations/203-drop-memory-items-tables.ts +33 -1
- package/src/memory/migrations/206-memory-graph-node-edits.ts +19 -0
- package/src/memory/migrations/206-scrub-corrupted-image-attachments.ts +131 -0
- package/src/memory/migrations/207-conversation-graph-memory-state.ts +20 -0
- package/src/memory/migrations/208-conversations-last-message-at.ts +35 -0
- package/src/memory/migrations/209-strip-thinking-from-consolidated.ts +85 -0
- package/src/memory/migrations/210-schedule-reuse-conversation.ts +13 -0
- package/src/memory/migrations/211-memory-recall-logs-query-context.ts +21 -0
- package/src/memory/migrations/212-llm-request-logs-created-at-index.ts +19 -0
- package/src/memory/migrations/index.ts +8 -0
- package/src/memory/migrations/registry.ts +8 -0
- package/src/memory/schema/conversations.ts +14 -0
- package/src/memory/schema/infrastructure.ts +8 -1
- package/src/memory/schema/memory-core.ts +0 -51
- package/src/memory/schema/memory-graph.ts +15 -0
- package/src/memory/task-memory-cleanup.ts +30 -11
- package/src/notifications/copy-composer.ts +86 -0
- package/src/notifications/decision-engine.ts +35 -0
- package/src/permissions/checker.ts +12 -1
- package/src/permissions/permission-mode-store.ts +180 -0
- package/src/permissions/permission-mode.ts +31 -0
- package/src/permissions/workspace-policy.ts +9 -0
- package/src/prompts/system-prompt.ts +59 -7
- package/src/prompts/templates/BOOTSTRAP-REFERENCE.md +100 -0
- package/src/prompts/templates/BOOTSTRAP.md +70 -165
- package/src/prompts/templates/HEARTBEAT.md +3 -1
- package/src/prompts/templates/SOUL.md +25 -4
- package/src/prompts/templates/UPDATES.md +8 -0
- package/src/providers/anthropic/client.ts +107 -219
- package/src/runtime/auth/route-policy.ts +23 -0
- package/src/runtime/http-server.ts +32 -2
- package/src/runtime/http-types.ts +12 -1
- package/src/runtime/migrations/vbundle-builder.ts +389 -3
- package/src/runtime/migrations/vbundle-importer.ts +8 -6
- package/src/runtime/routes/__tests__/user-route-dispatcher.test.ts +378 -0
- package/src/runtime/routes/app-management-routes.ts +1 -11
- package/src/runtime/routes/approval-strategies/guardian-callback-strategy.ts +26 -0
- package/src/runtime/routes/archive-utils.ts +29 -0
- package/src/runtime/routes/avatar-routes.ts +2 -9
- package/src/runtime/routes/btw-routes.ts +14 -1
- package/src/runtime/routes/conversation-analysis-routes.ts +173 -0
- package/src/runtime/routes/conversation-management-routes.ts +1 -14
- package/src/runtime/routes/conversation-query-routes.ts +49 -3
- package/src/runtime/routes/conversation-routes.ts +264 -44
- package/src/runtime/routes/heartbeat-routes.ts +4 -10
- package/src/runtime/routes/identity-routes.ts +53 -18
- package/src/runtime/routes/llm-context-normalization.ts +14 -10
- package/src/runtime/routes/log-export-routes.ts +23 -275
- package/src/runtime/routes/memory-item-routes.test.ts +168 -233
- package/src/runtime/routes/migration-routes.ts +18 -7
- package/src/runtime/routes/profiler-routes.ts +350 -0
- package/src/runtime/routes/schedule-routes.ts +27 -12
- package/src/runtime/routes/settings-routes.ts +95 -8
- package/src/runtime/routes/subagents-routes.ts +28 -7
- package/src/runtime/routes/user-route-dispatcher.ts +223 -0
- package/src/runtime/routes/user-routes.ts +41 -0
- package/src/runtime/routes/workspace-routes.ts +0 -1
- package/src/schedule/schedule-store.ts +30 -0
- package/src/schedule/scheduler.ts +45 -18
- package/src/skills/catalog-install.ts +10 -2
- package/src/skills/managed-store.ts +2 -2
- package/src/skills/skill-memory.ts +1 -293
- package/src/subagent/index.ts +13 -3
- package/src/subagent/manager.ts +308 -29
- package/src/subagent/types.ts +68 -0
- package/src/tasks/task-runner.ts +4 -4
- package/src/tools/apps/executors.ts +29 -4
- package/src/tools/filesystem/list.ts +93 -0
- package/src/tools/permission-checker.ts +78 -0
- package/src/tools/registry.ts +4 -0
- package/src/tools/schedule/create.ts +3 -0
- package/src/tools/schedule/list.ts +1 -0
- package/src/tools/schedule/update.ts +6 -0
- package/src/tools/shared/filesystem/errors.ts +5 -0
- package/src/tools/shared/filesystem/file-ops-service.ts +90 -2
- package/src/tools/shared/filesystem/types.ts +17 -0
- package/src/tools/shared/shell-output.ts +31 -2
- package/src/tools/subagent/abort.ts +12 -2
- package/src/tools/subagent/message.ts +9 -2
- package/src/tools/subagent/notify-parent.ts +79 -0
- package/src/tools/subagent/read.ts +29 -8
- package/src/tools/subagent/resolve.ts +21 -0
- package/src/tools/subagent/spawn.ts +2 -0
- package/src/tools/subagent/status.ts +11 -1
- package/src/tools/system/avatar-generator.ts +3 -3
- package/src/tools/system/register.ts +23 -0
- package/src/tools/system/set-permission-mode.ts +103 -0
- package/src/tools/terminal/parser.ts +30 -5
- package/src/tools/terminal/safe-env.ts +16 -1
- package/src/tools/tool-manifest.ts +6 -0
- package/src/tools/types.ts +2 -0
- package/src/util/logger.ts +1 -1
- package/src/util/platform.ts +50 -17
- package/src/workspace/migrations/023-move-config-files-to-workspace.ts +2 -2
- package/src/workspace/migrations/024-move-runtime-files-to-workspace.ts +2 -2
- package/src/workspace/migrations/028-recover-conversations-from-disk-view.ts +270 -0
- package/src/workspace/migrations/029-seed-pkb.ts +84 -0
- package/src/workspace/migrations/registry.ts +4 -0
- package/src/workspace/top-level-renderer.ts +5 -9
- package/src/__tests__/cli-memory.test.ts +0 -377
- package/src/__tests__/clipboard.test.ts +0 -88
- package/src/cli/cli-memory.ts +0 -179
- package/src/util/clipboard.ts +0 -34
|
@@ -3,6 +3,9 @@ import { join } from "node:path";
|
|
|
3
3
|
|
|
4
4
|
import type { Command } from "commander";
|
|
5
5
|
|
|
6
|
+
import { getConfig } from "../../config/loader.js";
|
|
7
|
+
import { resolveSkillStates } from "../../config/skill-state.js";
|
|
8
|
+
import { loadSkillCatalog } from "../../config/skills.js";
|
|
6
9
|
import type { CatalogSkill } from "../../skills/catalog-install.js";
|
|
7
10
|
import {
|
|
8
11
|
fetchCatalog,
|
|
@@ -56,13 +59,13 @@ Examples:
|
|
|
56
59
|
|
|
57
60
|
skills
|
|
58
61
|
.command("list")
|
|
59
|
-
.description("List
|
|
62
|
+
.description("List all skills (bundled, installed, and catalog)")
|
|
60
63
|
.option("--json", "Machine-readable JSON output")
|
|
61
64
|
.addHelpText(
|
|
62
65
|
"after",
|
|
63
66
|
`
|
|
64
|
-
Lists all skills
|
|
65
|
-
|
|
67
|
+
Lists all skills: bundled (compiled-in), installed (user-added), and
|
|
68
|
+
available catalog skills with their ID, name, and description.
|
|
66
69
|
|
|
67
70
|
Examples:
|
|
68
71
|
$ assistant skills list
|
|
@@ -70,34 +73,103 @@ Examples:
|
|
|
70
73
|
)
|
|
71
74
|
.action(async (opts: { json?: boolean }) => {
|
|
72
75
|
try {
|
|
73
|
-
//
|
|
74
|
-
|
|
76
|
+
// ── Bundled + installed skills (from loadSkillCatalog) ────────
|
|
77
|
+
const localCatalog = loadSkillCatalog();
|
|
78
|
+
const config = getConfig();
|
|
79
|
+
const resolved = resolveSkillStates(localCatalog, config);
|
|
80
|
+
const bundled = resolved.filter(
|
|
81
|
+
(r) => r.summary.source === "bundled",
|
|
82
|
+
);
|
|
83
|
+
const installed = resolved.filter(
|
|
84
|
+
(r) =>
|
|
85
|
+
r.summary.source === "managed" ||
|
|
86
|
+
r.summary.source === "workspace" ||
|
|
87
|
+
r.summary.source === "extra",
|
|
88
|
+
);
|
|
89
|
+
|
|
90
|
+
// ── Remote catalog skills ────────────────────────────────────
|
|
75
91
|
const repoSkillsDir = getRepoSkillsDir();
|
|
76
|
-
let
|
|
92
|
+
let remoteCatalog: CatalogSkill[];
|
|
77
93
|
if (repoSkillsDir) {
|
|
78
|
-
|
|
94
|
+
remoteCatalog = readLocalCatalog(repoSkillsDir);
|
|
79
95
|
} else {
|
|
80
|
-
|
|
96
|
+
remoteCatalog = await fetchCatalog();
|
|
81
97
|
}
|
|
98
|
+
// Exclude catalog skills that are already installed/bundled
|
|
99
|
+
const localIds = new Set(localCatalog.map((s) => s.id));
|
|
100
|
+
const availableCatalog = remoteCatalog.filter(
|
|
101
|
+
(s) => !localIds.has(s.id),
|
|
102
|
+
);
|
|
103
|
+
|
|
104
|
+
const totalCount =
|
|
105
|
+
bundled.length + installed.length + availableCatalog.length;
|
|
82
106
|
|
|
83
107
|
if (opts.json) {
|
|
84
|
-
|
|
108
|
+
const bundledJson = bundled.map((r) => ({
|
|
109
|
+
id: r.summary.id,
|
|
110
|
+
name: r.summary.displayName,
|
|
111
|
+
description: r.summary.description,
|
|
112
|
+
emoji: r.summary.emoji,
|
|
113
|
+
state: r.state,
|
|
114
|
+
}));
|
|
115
|
+
const installedJson = installed.map((r) => ({
|
|
116
|
+
id: r.summary.id,
|
|
117
|
+
name: r.summary.displayName,
|
|
118
|
+
description: r.summary.description,
|
|
119
|
+
emoji: r.summary.emoji,
|
|
120
|
+
state: r.state,
|
|
121
|
+
}));
|
|
122
|
+
console.log(
|
|
123
|
+
JSON.stringify({
|
|
124
|
+
ok: true,
|
|
125
|
+
skills: [...bundledJson, ...installedJson, ...availableCatalog],
|
|
126
|
+
bundled: bundledJson,
|
|
127
|
+
installed: installedJson,
|
|
128
|
+
catalog: availableCatalog,
|
|
129
|
+
}),
|
|
130
|
+
);
|
|
85
131
|
return;
|
|
86
132
|
}
|
|
87
133
|
|
|
88
|
-
if (
|
|
89
|
-
log.info("No skills available
|
|
134
|
+
if (totalCount === 0) {
|
|
135
|
+
log.info("No skills available.");
|
|
90
136
|
return;
|
|
91
137
|
}
|
|
92
138
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
const
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
: "";
|
|
99
|
-
|
|
100
|
-
|
|
139
|
+
if (bundled.length > 0) {
|
|
140
|
+
log.info(`Bundled skills (${bundled.length}):\n`);
|
|
141
|
+
for (const r of bundled) {
|
|
142
|
+
const s = r.summary;
|
|
143
|
+
const emoji = s.emoji ? `${s.emoji} ` : "";
|
|
144
|
+
const state = r.state === "disabled" ? " [disabled]" : "";
|
|
145
|
+
log.info(` ${emoji}${s.id}${state}`);
|
|
146
|
+
log.info(` ${s.displayName} — ${s.description}`);
|
|
147
|
+
}
|
|
148
|
+
log.info("");
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
if (installed.length > 0) {
|
|
152
|
+
log.info(`Installed skills (${installed.length}):\n`);
|
|
153
|
+
for (const r of installed) {
|
|
154
|
+
const s = r.summary;
|
|
155
|
+
const emoji = s.emoji ? `${s.emoji} ` : "";
|
|
156
|
+
const state = r.state === "disabled" ? " [disabled]" : "";
|
|
157
|
+
log.info(` ${emoji}${s.id}${state}`);
|
|
158
|
+
log.info(` ${s.displayName} — ${s.description}`);
|
|
159
|
+
}
|
|
160
|
+
log.info("");
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
if (availableCatalog.length > 0) {
|
|
164
|
+
log.info(`Available catalog skills (${availableCatalog.length}):\n`);
|
|
165
|
+
for (const s of availableCatalog) {
|
|
166
|
+
const emoji = s.emoji ? `${s.emoji} ` : "";
|
|
167
|
+
const deps = s.includes?.length
|
|
168
|
+
? ` (requires: ${s.includes.join(", ")})`
|
|
169
|
+
: "";
|
|
170
|
+
log.info(` ${emoji}${s.id}`);
|
|
171
|
+
log.info(` ${s.name} — ${s.description}${deps}`);
|
|
172
|
+
}
|
|
101
173
|
}
|
|
102
174
|
} catch (err) {
|
|
103
175
|
const msg = err instanceof Error ? err.message : String(err);
|
|
@@ -139,6 +211,14 @@ Examples:
|
|
|
139
211
|
const limit = parseInt(opts.limit, 10) || 10;
|
|
140
212
|
|
|
141
213
|
try {
|
|
214
|
+
// ── Bundled + installed skill search ─────────────────────────
|
|
215
|
+
const localCatalog = loadSkillCatalog();
|
|
216
|
+
const bundledMatches = filterByQuery(localCatalog, query, [
|
|
217
|
+
(s) => s.id,
|
|
218
|
+
(s) => s.displayName,
|
|
219
|
+
(s) => s.description,
|
|
220
|
+
]);
|
|
221
|
+
|
|
142
222
|
// ── Vellum catalog search ────────────────────────────────────
|
|
143
223
|
const repoSkillsDir = getRepoSkillsDir();
|
|
144
224
|
let catalog: CatalogSkill[];
|
|
@@ -151,8 +231,11 @@ Examples:
|
|
|
151
231
|
catalog = [];
|
|
152
232
|
}
|
|
153
233
|
}
|
|
234
|
+
// Exclude catalog entries that match a bundled/installed skill
|
|
235
|
+
const localIds = new Set(localCatalog.map((s) => s.id));
|
|
236
|
+
const filteredCatalog = catalog.filter((s) => !localIds.has(s.id));
|
|
154
237
|
|
|
155
|
-
const catalogMatches = filterByQuery(
|
|
238
|
+
const catalogMatches = filterByQuery(filteredCatalog, query, [
|
|
156
239
|
(s) => s.id,
|
|
157
240
|
(s) => s.name,
|
|
158
241
|
(s) => s.description,
|
|
@@ -202,6 +285,7 @@ Examples:
|
|
|
202
285
|
]);
|
|
203
286
|
|
|
204
287
|
if (
|
|
288
|
+
bundledMatches.length === 0 &&
|
|
205
289
|
catalogMatches.length === 0 &&
|
|
206
290
|
registryResults.length === 0 &&
|
|
207
291
|
clawhubResults.length === 0
|
|
@@ -210,6 +294,7 @@ Examples:
|
|
|
210
294
|
console.log(
|
|
211
295
|
JSON.stringify({
|
|
212
296
|
ok: true,
|
|
297
|
+
bundled: [],
|
|
213
298
|
catalog: [],
|
|
214
299
|
community: [],
|
|
215
300
|
clawhub: [],
|
|
@@ -255,6 +340,13 @@ Examples:
|
|
|
255
340
|
console.log(
|
|
256
341
|
JSON.stringify({
|
|
257
342
|
ok: true,
|
|
343
|
+
bundled: bundledMatches.map((s) => ({
|
|
344
|
+
id: s.id,
|
|
345
|
+
name: s.displayName,
|
|
346
|
+
description: s.description,
|
|
347
|
+
emoji: s.emoji,
|
|
348
|
+
source: s.source,
|
|
349
|
+
})),
|
|
258
350
|
catalog: catalogMatches,
|
|
259
351
|
community: registryResults,
|
|
260
352
|
clawhub: clawhubResults,
|
|
@@ -271,6 +363,24 @@ Examples:
|
|
|
271
363
|
const isInstalled = (id: string) =>
|
|
272
364
|
existsSync(join(skillsDir, id, "SKILL.md"));
|
|
273
365
|
|
|
366
|
+
// ── Display bundled/installed results ─────────────────────────
|
|
367
|
+
if (bundledMatches.length > 0) {
|
|
368
|
+
log.info(
|
|
369
|
+
`Bundled & installed skills (${bundledMatches.length}):\n`,
|
|
370
|
+
);
|
|
371
|
+
for (const s of bundledMatches) {
|
|
372
|
+
const emoji = s.emoji ? `${s.emoji} ` : "";
|
|
373
|
+
const tag = s.source === "bundled" ? " [bundled]" : " [installed]";
|
|
374
|
+
log.info(` ${emoji}${s.displayName}${tag}`);
|
|
375
|
+
if (s.displayName !== s.id) {
|
|
376
|
+
log.info(` ID: ${s.id}`);
|
|
377
|
+
}
|
|
378
|
+
log.info(` ${s.description}`);
|
|
379
|
+
log.info(` Load: skill_load skill=${s.id}`);
|
|
380
|
+
log.info("");
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
|
|
274
384
|
// ── Display catalog results ──────────────────────────────────
|
|
275
385
|
if (catalogMatches.length > 0) {
|
|
276
386
|
log.info(`Vellum catalog (${catalogMatches.length}):\n`);
|
package/src/cli/program.ts
CHANGED
|
@@ -26,6 +26,7 @@ import { registerMemoryCommand } from "./commands/memory.js";
|
|
|
26
26
|
import { registerNotificationsCommand } from "./commands/notifications.js";
|
|
27
27
|
import { registerOAuthCommand } from "./commands/oauth/index.js";
|
|
28
28
|
import { registerPlatformCommand } from "./commands/platform/index.js";
|
|
29
|
+
import { registerRoutesCommand } from "./commands/routes.js";
|
|
29
30
|
import { registerSequenceCommand } from "./commands/sequence.js";
|
|
30
31
|
import { registerShotgunCommand } from "./commands/shotgun.js";
|
|
31
32
|
import { registerSkillsCommand } from "./commands/skills.js";
|
|
@@ -75,6 +76,7 @@ Examples:
|
|
|
75
76
|
registerNotificationsCommand(program);
|
|
76
77
|
registerPlatformCommand(program);
|
|
77
78
|
registerOAuthCommand(program);
|
|
79
|
+
registerRoutesCommand(program);
|
|
78
80
|
registerSkillsCommand(program);
|
|
79
81
|
registerBrowserRelayCommand(program);
|
|
80
82
|
registerUsageCommand(program);
|
package/src/cli.ts
CHANGED
|
@@ -33,11 +33,6 @@ import {
|
|
|
33
33
|
type EventStreamWatcher,
|
|
34
34
|
watchEventStream,
|
|
35
35
|
} from "./signals/event-stream.js";
|
|
36
|
-
import {
|
|
37
|
-
copyToClipboard,
|
|
38
|
-
extractLastCodeBlock,
|
|
39
|
-
formatConversationForExport,
|
|
40
|
-
} from "./util/clipboard.js";
|
|
41
36
|
import { formatDiff, formatNewFileDiff } from "./util/diff.js";
|
|
42
37
|
import { getHistoryPath, getSignalsDir } from "./util/platform.js";
|
|
43
38
|
import { Spinner } from "./util/spinner.js";
|
|
@@ -143,7 +138,6 @@ export async function startCli(): Promise<void> {
|
|
|
143
138
|
let conversationId = "";
|
|
144
139
|
let pendingUserContent: string | null = null;
|
|
145
140
|
let generating = false;
|
|
146
|
-
let lastResponse = "";
|
|
147
141
|
let lastUsage: {
|
|
148
142
|
inputTokens: number;
|
|
149
143
|
outputTokens: number;
|
|
@@ -153,7 +147,6 @@ export async function startCli(): Promise<void> {
|
|
|
153
147
|
model: string;
|
|
154
148
|
} | null = null;
|
|
155
149
|
let pendingSessionPick = false;
|
|
156
|
-
let pendingCopySession = false;
|
|
157
150
|
let toolStreaming = false;
|
|
158
151
|
let lastDisplayedError: string | null = null;
|
|
159
152
|
let eventSubscription: EventStreamWatcher | null = null;
|
|
@@ -398,7 +391,6 @@ export async function startCli(): Promise<void> {
|
|
|
398
391
|
if (pendingUserContent) {
|
|
399
392
|
const content = pendingUserContent;
|
|
400
393
|
pendingUserContent = null;
|
|
401
|
-
lastResponse = "";
|
|
402
394
|
sendUserMessage(content).then((result) => {
|
|
403
395
|
if (result.ok) {
|
|
404
396
|
generating = true;
|
|
@@ -408,7 +400,6 @@ export async function startCli(): Promise<void> {
|
|
|
408
400
|
process.stdout.write(`${result.message}\n`);
|
|
409
401
|
rl.question("Send anyway? (y/N): ", (answer) => {
|
|
410
402
|
if (answer.trim().toLowerCase() === "y") {
|
|
411
|
-
lastResponse = "";
|
|
412
403
|
sendUserMessage(content, {
|
|
413
404
|
bypassSecretCheck: true,
|
|
414
405
|
}).then((retryResult) => {
|
|
@@ -439,7 +430,6 @@ export async function startCli(): Promise<void> {
|
|
|
439
430
|
|
|
440
431
|
case "assistant_text_delta":
|
|
441
432
|
spinner.stop();
|
|
442
|
-
lastResponse += msg.text;
|
|
443
433
|
process.stdout.write(msg.text);
|
|
444
434
|
break;
|
|
445
435
|
|
|
@@ -595,9 +585,8 @@ export async function startCli(): Promise<void> {
|
|
|
595
585
|
case "error":
|
|
596
586
|
spinner.stop();
|
|
597
587
|
generating = false;
|
|
598
|
-
if (pendingSessionPick
|
|
588
|
+
if (pendingSessionPick) {
|
|
599
589
|
pendingSessionPick = false;
|
|
600
|
-
pendingCopySession = false;
|
|
601
590
|
rl.removeAllListeners("line");
|
|
602
591
|
rl.on("line", handleLine);
|
|
603
592
|
}
|
|
@@ -646,26 +635,6 @@ export async function startCli(): Promise<void> {
|
|
|
646
635
|
break;
|
|
647
636
|
|
|
648
637
|
case "history_response":
|
|
649
|
-
if (pendingCopySession) {
|
|
650
|
-
pendingCopySession = false;
|
|
651
|
-
if (msg.messages.length === 0) {
|
|
652
|
-
process.stdout.write("\n No messages to copy.\n\n");
|
|
653
|
-
} else {
|
|
654
|
-
try {
|
|
655
|
-
const formatted = formatConversationForExport(msg.messages);
|
|
656
|
-
copyToClipboard(formatted);
|
|
657
|
-
process.stdout.write(
|
|
658
|
-
`\n Copied conversation (${msg.messages.length} messages) to clipboard.\n\n`,
|
|
659
|
-
);
|
|
660
|
-
} catch (err) {
|
|
661
|
-
process.stdout.write(
|
|
662
|
-
`\n Clipboard error: ${(err as Error).message}\n\n`,
|
|
663
|
-
);
|
|
664
|
-
}
|
|
665
|
-
}
|
|
666
|
-
prompt();
|
|
667
|
-
break;
|
|
668
|
-
}
|
|
669
638
|
process.stdout.write("\n");
|
|
670
639
|
if (msg.messages.length === 0) {
|
|
671
640
|
process.stdout.write(" No messages in this conversation.\n");
|
|
@@ -686,7 +655,6 @@ export async function startCli(): Promise<void> {
|
|
|
686
655
|
if (msg.removedCount === 0) {
|
|
687
656
|
process.stdout.write("\n Nothing to undo.\n\n");
|
|
688
657
|
} else {
|
|
689
|
-
lastResponse = "";
|
|
690
658
|
process.stdout.write(
|
|
691
659
|
`\n Removed last exchange (${msg.removedCount} messages).\n\n`,
|
|
692
660
|
);
|
|
@@ -754,21 +722,6 @@ export async function startCli(): Promise<void> {
|
|
|
754
722
|
/* ignore */
|
|
755
723
|
}
|
|
756
724
|
|
|
757
|
-
if (content === "/copy") {
|
|
758
|
-
if (!lastResponse) {
|
|
759
|
-
process.stdout.write("No response to copy.\n");
|
|
760
|
-
} else {
|
|
761
|
-
try {
|
|
762
|
-
copyToClipboard(lastResponse);
|
|
763
|
-
process.stdout.write("Copied to clipboard.\n");
|
|
764
|
-
} catch (err) {
|
|
765
|
-
process.stdout.write(`Clipboard error: ${(err as Error).message}\n`);
|
|
766
|
-
}
|
|
767
|
-
}
|
|
768
|
-
prompt();
|
|
769
|
-
return;
|
|
770
|
-
}
|
|
771
|
-
|
|
772
725
|
if (content === "/conversations") {
|
|
773
726
|
pendingSessionPick = true;
|
|
774
727
|
try {
|
|
@@ -787,65 +740,6 @@ export async function startCli(): Promise<void> {
|
|
|
787
740
|
return;
|
|
788
741
|
}
|
|
789
742
|
|
|
790
|
-
if (content === "/copy-code") {
|
|
791
|
-
const code = extractLastCodeBlock(lastResponse);
|
|
792
|
-
if (code == null) {
|
|
793
|
-
process.stdout.write("No code block found.\n");
|
|
794
|
-
} else {
|
|
795
|
-
try {
|
|
796
|
-
copyToClipboard(code);
|
|
797
|
-
process.stdout.write("Copied code block to clipboard.\n");
|
|
798
|
-
} catch (err) {
|
|
799
|
-
process.stdout.write(`Clipboard error: ${(err as Error).message}\n`);
|
|
800
|
-
}
|
|
801
|
-
}
|
|
802
|
-
prompt();
|
|
803
|
-
return;
|
|
804
|
-
}
|
|
805
|
-
|
|
806
|
-
if (content === "/copy-conversation") {
|
|
807
|
-
try {
|
|
808
|
-
const mapping = getConversationByKey(conversationKey);
|
|
809
|
-
if (!mapping) {
|
|
810
|
-
process.stdout.write("\n No messages to copy.\n\n");
|
|
811
|
-
prompt();
|
|
812
|
-
return;
|
|
813
|
-
}
|
|
814
|
-
const rawMessages = getMessages(mapping.conversationId);
|
|
815
|
-
if (rawMessages.length === 0) {
|
|
816
|
-
process.stdout.write("\n No messages to copy.\n\n");
|
|
817
|
-
} else {
|
|
818
|
-
const rendered = rawMessages.map((msg) => {
|
|
819
|
-
let parsedContent: unknown;
|
|
820
|
-
try {
|
|
821
|
-
parsedContent = JSON.parse(msg.content);
|
|
822
|
-
} catch {
|
|
823
|
-
parsedContent = msg.content;
|
|
824
|
-
}
|
|
825
|
-
return {
|
|
826
|
-
role: msg.role as "user" | "assistant",
|
|
827
|
-
text: renderHistoryContent(parsedContent).text,
|
|
828
|
-
};
|
|
829
|
-
});
|
|
830
|
-
try {
|
|
831
|
-
const formatted = formatConversationForExport(rendered);
|
|
832
|
-
copyToClipboard(formatted);
|
|
833
|
-
process.stdout.write(
|
|
834
|
-
`\n Copied conversation (${rawMessages.length} messages) to clipboard.\n\n`,
|
|
835
|
-
);
|
|
836
|
-
} catch (err) {
|
|
837
|
-
process.stdout.write(
|
|
838
|
-
`\n Clipboard error: ${(err as Error).message}\n\n`,
|
|
839
|
-
);
|
|
840
|
-
}
|
|
841
|
-
}
|
|
842
|
-
} catch {
|
|
843
|
-
process.stdout.write("[Failed to fetch history]\n");
|
|
844
|
-
}
|
|
845
|
-
prompt();
|
|
846
|
-
return;
|
|
847
|
-
}
|
|
848
|
-
|
|
849
743
|
if (content === "/new") {
|
|
850
744
|
// Create a new conversation by using a unique key
|
|
851
745
|
conversationKey = `builtin-cli:${randomUUID()}`;
|
|
@@ -859,7 +753,6 @@ export async function startCli(): Promise<void> {
|
|
|
859
753
|
}
|
|
860
754
|
|
|
861
755
|
if (content === "/clear") {
|
|
862
|
-
lastResponse = "";
|
|
863
756
|
process.stdout.write("\x1b[r");
|
|
864
757
|
process.stdout.write("\x1b[2J\x1b[H");
|
|
865
758
|
mainScreenLayout = renderMainScreen();
|
|
@@ -955,7 +848,6 @@ export async function startCli(): Promise<void> {
|
|
|
955
848
|
if (result.removedCount === 0) {
|
|
956
849
|
process.stdout.write("\n Nothing to undo.\n\n");
|
|
957
850
|
} else {
|
|
958
|
-
lastResponse = "";
|
|
959
851
|
process.stdout.write(
|
|
960
852
|
`\n Removed last exchange (${result.removedCount} messages).\n\n`,
|
|
961
853
|
);
|
|
@@ -1016,15 +908,6 @@ export async function startCli(): Promise<void> {
|
|
|
1016
908
|
" /undo Remove last message exchange\n",
|
|
1017
909
|
);
|
|
1018
910
|
process.stdout.write(" /usage Show token usage and cost\n");
|
|
1019
|
-
process.stdout.write(
|
|
1020
|
-
" /copy Copy last response to clipboard\n",
|
|
1021
|
-
);
|
|
1022
|
-
process.stdout.write(
|
|
1023
|
-
" /copy-code Copy last code block to clipboard\n",
|
|
1024
|
-
);
|
|
1025
|
-
process.stdout.write(
|
|
1026
|
-
" /copy-conversation Copy entire conversation to clipboard\n",
|
|
1027
|
-
);
|
|
1028
911
|
process.stdout.write(" /help Show this help\n");
|
|
1029
912
|
process.stdout.write("\n");
|
|
1030
913
|
prompt();
|
|
@@ -1032,14 +915,12 @@ export async function startCli(): Promise<void> {
|
|
|
1032
915
|
}
|
|
1033
916
|
|
|
1034
917
|
// Regular user message
|
|
1035
|
-
lastResponse = "";
|
|
1036
918
|
sendUserMessage(content).then((result) => {
|
|
1037
919
|
if (!result.ok) {
|
|
1038
920
|
if (result.error === "secret_blocked" && result.message) {
|
|
1039
921
|
process.stdout.write(`${result.message}\n`);
|
|
1040
922
|
rl.question("Send anyway? (y/N): ", (answer) => {
|
|
1041
923
|
if (answer.trim().toLowerCase() === "y") {
|
|
1042
|
-
lastResponse = "";
|
|
1043
924
|
sendUserMessage(content, { bypassSecretCheck: true }).then(
|
|
1044
925
|
(retryResult) => {
|
|
1045
926
|
if (retryResult.ok) {
|
|
@@ -238,7 +238,7 @@ export const Header: FunctionComponent<HeaderProps> = ({ title, count }) => (
|
|
|
238
238
|
|
|
239
239
|
file_write("{workspaceDir}/data/apps/project-tracker/src/styles.css", `.app { padding: var(--v-spacing-lg); }
|
|
240
240
|
.header { display: flex; justify-content: space-between; align-items: center; }
|
|
241
|
-
.badge { background: var(--v-accent); color: white; padding: var(--v-spacing-xs) var(--v-spacing-sm); border-radius: var(--v-radius-pill); }`)
|
|
241
|
+
.badge { background: var(--v-accent); color: var(--v-aux-white); padding: var(--v-spacing-xs) var(--v-spacing-sm); border-radius: var(--v-radius-pill); }`)
|
|
242
242
|
|
|
243
243
|
# After all files are written, compile and refresh:
|
|
244
244
|
app_refresh(app_id)
|
|
@@ -290,9 +290,12 @@ Available design tokens:
|
|
|
290
290
|
| **Typography** | `--v-font-family`, `--v-font-mono`, `--v-font-size-xs` (10px) / `-sm` (11px) / `-base` (14px) / `-lg` (17px) / `-xl` (22px) / `-2xl` (26px), `--v-line-height` |
|
|
291
291
|
| **Animation** | `--v-duration-fast` (0.15s) / `-standard` (0.25s) / `-slow` (0.4s) |
|
|
292
292
|
| **Palettes** | `--v-slate-{950..50}`, `--v-emerald-*`, `--v-violet-*`, `--v-indigo-*`, `--v-rose-*`, `--v-amber-*` |
|
|
293
|
+
| **Constant** | `--v-aux-white` (always `#FFFFFF` in both modes — use for text on filled/accent backgrounds) |
|
|
293
294
|
|
|
294
295
|
Utility classes: `.v-button` (`.secondary`/`.danger`/`.ghost`), `.v-card`, `.v-list`/`.v-list-item`, `.v-badge` (`.success`/`.warning`/`.danger`), `.v-input-row`, `.v-empty-state`, `.v-toggle`.
|
|
295
296
|
|
|
297
|
+
**Never hardcode `color: white` or `color: #fff`.** Use `var(--v-aux-white)` for text on filled/accent backgrounds, or `var(--v-text)` / `var(--v-text-secondary)` for text on surface backgrounds. Hardcoded white causes invisible text on light surfaces.
|
|
298
|
+
|
|
296
299
|
**Custom themes:** When the user wants a specific branded look, write complete CSS with hardcoded colors and `@media (prefers-color-scheme: dark)` for dark variants. Don't mix `--v-*` auto-switching variables with hardcoded colors in the same element.
|
|
297
300
|
|
|
298
301
|
**Theme detection in JavaScript:**
|
|
@@ -156,9 +156,9 @@ Scan tools (`gmail_sender_digest`, `gmail_outreach_scan`) return a `scan_id` tha
|
|
|
156
156
|
|
|
157
157
|
Before composing any email that references a date or time:
|
|
158
158
|
|
|
159
|
-
1. Check the
|
|
159
|
+
1. Check the `timestamp:` field in the `<turn_context>` block for today's date and timezone
|
|
160
160
|
2. Verify that "tomorrow" means the day after today's date, "next week" means the upcoming Monday–Friday, etc.
|
|
161
|
-
3. If the email references a date from another message, cross-check it against the
|
|
161
|
+
3. If the email references a date from another message, cross-check it against the turn context to ensure it's in the future
|
|
162
162
|
|
|
163
163
|
## Confidence Scores
|
|
164
164
|
|
|
@@ -148,6 +148,7 @@ Telegram is supported as a messaging provider with limited capabilities compared
|
|
|
148
148
|
- `send_notification` is provided by the **notifications** skill (always active) -- use it when the user asks for an alert/notification (for example "send this as a desktop notification").
|
|
149
149
|
- Use `messaging_send` when the user asks to send a message into a specific chat/email destination.
|
|
150
150
|
- `send_notification` channel routing is LLM-driven; `preferred_channels` are hints, not hard channel forcing.
|
|
151
|
+
- Before using `messaging_send` or `send_notification`, look up the recipient's contact record with `contact_search` to inform tone and content (see **Recipient Context** below).
|
|
151
152
|
|
|
152
153
|
## Personalized Drafting
|
|
153
154
|
|
|
@@ -157,6 +158,12 @@ If no style items exist and the user asks you to draft a message, suggest runnin
|
|
|
157
158
|
|
|
158
159
|
> "I can analyze your sent messages to learn your writing style so drafts sound like you. Want me to do that?"
|
|
159
160
|
|
|
161
|
+
## Recipient Context
|
|
162
|
+
|
|
163
|
+
Before composing or sending a message to someone, look up their contact record with `contact_search` using their name or channel address. If the contact has notes (e.g. relationship context, communication preferences, response expectations), use that context to inform the message's tone, level of detail, and content. This ensures outbound messages are personalized to the recipient — not just the sender's style.
|
|
164
|
+
|
|
165
|
+
If no contact record exists, proceed without recipient context.
|
|
166
|
+
|
|
160
167
|
## Confidence Scores
|
|
161
168
|
|
|
162
169
|
Medium and high risk tools require a confidence score between 0 and 1:
|
|
@@ -87,6 +87,14 @@ The `mode` parameter controls what happens when a schedule fires:
|
|
|
87
87
|
|
|
88
88
|
Use `notify` for simple reminders ("remind me to take medicine at 9am") and `execute` for tasks that need assistant action ("check my calendar at 8am and send me a digest").
|
|
89
89
|
|
|
90
|
+
## Conversation Reuse
|
|
91
|
+
|
|
92
|
+
By default, each schedule run creates a new conversation. For recurring schedules that benefit from accumulating context across runs (e.g. polling-style jobs, daily digests that reference prior results), set `reuse_conversation: true`. When enabled, subsequent runs reuse the conversation from the last successful run instead of creating a new one.
|
|
93
|
+
|
|
94
|
+
- Only applies to **recurring** schedules; ignored for one-shot schedules.
|
|
95
|
+
- If the prior conversation has been deleted, a new one is created automatically.
|
|
96
|
+
- On the first run (no prior conversation), a new conversation is created as usual.
|
|
97
|
+
|
|
90
98
|
## Routing (notify mode)
|
|
91
99
|
|
|
92
100
|
Control how notify-mode schedules are delivered at trigger time with `routing_intent`:
|
|
@@ -101,9 +109,20 @@ Optionally pass `routing_hints` (a JSON object) to influence routing decisions (
|
|
|
101
109
|
|
|
102
110
|
- **Default to `all_channels`** for most notifications. Users usually want to be notified wherever they are.
|
|
103
111
|
- **Use `single_channel`** only when the user explicitly specifies a single channel (e.g. "remind me on Telegram").
|
|
104
|
-
- **
|
|
112
|
+
- **Determine the originating channel** for routing hints using this priority:
|
|
113
|
+
1. **`source_channel`** from `<turn_context>` — use directly if present. This is the authoritative channel name.
|
|
114
|
+
2. **`interface` fallback** — if `source_channel` is absent (common for guardian/direct users), map the `interface` value to a channel name:
|
|
115
|
+
| `interface` value | Channel name |
|
|
116
|
+
| --- | --- |
|
|
117
|
+
| `macos`, `ios` | `vellum` |
|
|
118
|
+
| `telegram` | `telegram` |
|
|
119
|
+
| `slack` | `slack` |
|
|
120
|
+
| `cli` | _(omit — no routable channel)_ |
|
|
121
|
+
3. If neither field is present or the interface is `cli`, omit `preferred_channels`.
|
|
122
|
+
|
|
123
|
+
When a channel is determined, include it as a routing hint:
|
|
105
124
|
```
|
|
106
|
-
routing_hints: { preferred_channels: ["
|
|
125
|
+
routing_hints: { preferred_channels: ["<resolved channel>"] }
|
|
107
126
|
routing_intent: "all_channels"
|
|
108
127
|
```
|
|
109
128
|
|
|
@@ -119,6 +138,7 @@ Use `syntax` + `expression` to specify the schedule type explicitly, or just `ex
|
|
|
119
138
|
|
|
120
139
|
## Tips
|
|
121
140
|
|
|
141
|
+
- **When the user specifies a name for the schedule, use it exactly as given.** Do not paraphrase, embellish, or generate a descriptive name.
|
|
122
142
|
- Use `schedule_create` for both recurring automation ("every day at 9am") and one-time reminders ("remind me at 3pm").
|
|
123
143
|
- For task tracking ("add to my tasks", "add to my queue"), use task_list_add instead.
|
|
124
144
|
- `fire_at` must be a strict ISO 8601 timestamp with timezone offset or Z (e.g. `2025-03-15T09:00:00-05:00`).
|
|
@@ -56,6 +56,10 @@
|
|
|
56
56
|
"type": "boolean",
|
|
57
57
|
"description": "When true, suppress completion notifications for this schedule. The job still runs and produces output, but no notification or conversation message is sent on completion. Useful for high-frequency recurring jobs that report findings separately. Defaults to false."
|
|
58
58
|
},
|
|
59
|
+
"reuse_conversation": {
|
|
60
|
+
"type": "boolean",
|
|
61
|
+
"description": "When true, reuse the same conversation across recurring schedule runs instead of creating a new one each time. Useful for polling-style schedules that accumulate context over time. Ignored for one-shot schedules. Defaults to false."
|
|
62
|
+
},
|
|
59
63
|
"activity": {
|
|
60
64
|
"type": "string",
|
|
61
65
|
"description": "Brief non-technical explanation of why this tool is being called"
|
|
@@ -147,6 +151,10 @@
|
|
|
147
151
|
"type": "boolean",
|
|
148
152
|
"description": "When true, suppress completion notifications for this schedule. Useful for high-frequency jobs that report findings separately."
|
|
149
153
|
},
|
|
154
|
+
"reuse_conversation": {
|
|
155
|
+
"type": "boolean",
|
|
156
|
+
"description": "When true, reuse the same conversation across recurring schedule runs instead of creating a new one each time. Useful for polling-style schedules that accumulate context over time. Ignored for one-shot schedules."
|
|
157
|
+
},
|
|
150
158
|
"activity": {
|
|
151
159
|
"type": "string",
|
|
152
160
|
"description": "Brief non-technical explanation of why this tool is being called"
|
|
@@ -7,27 +7,17 @@ import type {
|
|
|
7
7
|
ToolContext,
|
|
8
8
|
ToolExecutionResult,
|
|
9
9
|
} from "../../../../tools/types.js";
|
|
10
|
-
import {
|
|
10
|
+
import { getAvatarDir, getAvatarImagePath } from "../../../../util/platform.js";
|
|
11
11
|
|
|
12
12
|
export async function run(
|
|
13
13
|
_input: Record<string, unknown>,
|
|
14
14
|
_context: ToolContext,
|
|
15
15
|
): Promise<ToolExecutionResult> {
|
|
16
|
-
const avatarPath =
|
|
17
|
-
getWorkspaceDir(),
|
|
18
|
-
"data",
|
|
19
|
-
"avatar",
|
|
20
|
-
"avatar-image.png",
|
|
21
|
-
);
|
|
16
|
+
const avatarPath = getAvatarImagePath();
|
|
22
17
|
|
|
23
18
|
if (!existsSync(avatarPath)) {
|
|
24
19
|
// Check for native character traits and regenerate the static PNG
|
|
25
|
-
const traitsPath = join(
|
|
26
|
-
getWorkspaceDir(),
|
|
27
|
-
"data",
|
|
28
|
-
"avatar",
|
|
29
|
-
"character-traits.json",
|
|
30
|
-
);
|
|
20
|
+
const traitsPath = join(getAvatarDir(), "character-traits.json");
|
|
31
21
|
if (existsSync(traitsPath)) {
|
|
32
22
|
try {
|
|
33
23
|
const traits = JSON.parse(readFileSync(traitsPath, "utf-8"));
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { existsSync, unlinkSync } from "node:fs";
|
|
2
|
-
import { join } from "node:path";
|
|
3
2
|
|
|
4
3
|
import { buildAssistantEvent } from "../../../../runtime/assistant-event.js";
|
|
5
4
|
import { assistantEventHub } from "../../../../runtime/assistant-event-hub.js";
|
|
@@ -9,7 +8,7 @@ import type {
|
|
|
9
8
|
ToolExecutionResult,
|
|
10
9
|
} from "../../../../tools/types.js";
|
|
11
10
|
import { getLogger } from "../../../../util/logger.js";
|
|
12
|
-
import {
|
|
11
|
+
import { getAvatarImagePath } from "../../../../util/platform.js";
|
|
13
12
|
import { updateIdentityAvatarSection } from "./identity-avatar.js";
|
|
14
13
|
|
|
15
14
|
const log = getLogger("avatar-remove");
|
|
@@ -18,8 +17,7 @@ export async function run(
|
|
|
18
17
|
_input: Record<string, unknown>,
|
|
19
18
|
_context: ToolContext,
|
|
20
19
|
): Promise<ToolExecutionResult> {
|
|
21
|
-
const
|
|
22
|
-
const avatarPath = join(avatarDir, "avatar-image.png");
|
|
20
|
+
const avatarPath = getAvatarImagePath();
|
|
23
21
|
|
|
24
22
|
if (!existsSync(avatarPath)) {
|
|
25
23
|
return {
|