@vellumai/assistant 0.4.49 → 0.4.50
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/ARCHITECTURE.md +24 -33
- package/README.md +3 -3
- package/docs/architecture/memory.md +180 -119
- package/package.json +2 -2
- package/src/__tests__/agent-loop.test.ts +3 -1
- package/src/__tests__/anthropic-provider.test.ts +114 -23
- package/src/__tests__/approval-cascade.test.ts +1 -15
- package/src/__tests__/approval-routes-http.test.ts +2 -0
- package/src/__tests__/assistant-feature-flag-guard.test.ts +0 -23
- package/src/__tests__/canonical-guardian-store.test.ts +95 -0
- package/src/__tests__/checker.test.ts +13 -0
- package/src/__tests__/config-schema.test.ts +1 -68
- package/src/__tests__/context-memory-e2e.test.ts +11 -100
- package/src/__tests__/conversation-routes-guardian-reply.test.ts +8 -0
- package/src/__tests__/conversation-routes-slash-commands.test.ts +1 -0
- package/src/__tests__/credential-security-e2e.test.ts +1 -0
- package/src/__tests__/credential-vault-unit.test.ts +4 -0
- package/src/__tests__/credential-vault.test.ts +13 -1
- package/src/__tests__/cu-unified-flow.test.ts +532 -0
- package/src/__tests__/date-context.test.ts +93 -77
- package/src/__tests__/deterministic-verification-control-plane.test.ts +64 -0
- package/src/__tests__/guardian-routing-invariants.test.ts +93 -0
- package/src/__tests__/history-repair.test.ts +245 -0
- package/src/__tests__/host-cu-proxy.test.ts +165 -3
- package/src/__tests__/http-user-message-parity.test.ts +1 -0
- package/src/__tests__/invite-redemption-service.test.ts +65 -1
- package/src/__tests__/keychain-broker-client.test.ts +4 -4
- package/src/__tests__/memory-context-benchmark.benchmark.test.ts +56 -18
- package/src/__tests__/memory-lifecycle-e2e.test.ts +244 -387
- package/src/__tests__/memory-recall-quality.test.ts +244 -407
- package/src/__tests__/memory-regressions.experimental.test.ts +126 -101
- package/src/__tests__/memory-regressions.test.ts +477 -2841
- package/src/__tests__/memory-retrieval.benchmark.test.ts +33 -150
- package/src/__tests__/memory-upsert-concurrency.test.ts +5 -244
- package/src/__tests__/mime-builder.test.ts +28 -0
- package/src/__tests__/native-web-search.test.ts +1 -0
- package/src/__tests__/oauth-cli.test.ts +572 -5
- package/src/__tests__/oauth-store.test.ts +120 -6
- package/src/__tests__/qdrant-collection-migration.test.ts +53 -8
- package/src/__tests__/registry.test.ts +0 -1
- package/src/__tests__/relay-server.test.ts +46 -1
- package/src/__tests__/schedule-tools.test.ts +32 -0
- package/src/__tests__/script-proxy-certs.test.ts +1 -1
- package/src/__tests__/secret-onetime-send.test.ts +1 -0
- package/src/__tests__/secure-keys.test.ts +7 -2
- package/src/__tests__/send-endpoint-busy.test.ts +3 -0
- package/src/__tests__/session-abort-tool-results.test.ts +1 -14
- package/src/__tests__/session-agent-loop-overflow.test.ts +1583 -0
- package/src/__tests__/session-agent-loop.test.ts +19 -15
- package/src/__tests__/session-confirmation-signals.test.ts +1 -15
- package/src/__tests__/session-error.test.ts +124 -2
- package/src/__tests__/session-history-web-search.test.ts +918 -0
- package/src/__tests__/session-pre-run-repair.test.ts +1 -14
- package/src/__tests__/session-provider-retry-repair.test.ts +25 -28
- package/src/__tests__/session-queue.test.ts +37 -27
- package/src/__tests__/session-runtime-assembly.test.ts +54 -0
- package/src/__tests__/session-slash-known.test.ts +1 -15
- package/src/__tests__/session-slash-queue.test.ts +1 -15
- package/src/__tests__/session-slash-unknown.test.ts +1 -15
- package/src/__tests__/session-workspace-cache-state.test.ts +3 -33
- package/src/__tests__/session-workspace-injection.test.ts +3 -37
- package/src/__tests__/session-workspace-tool-tracking.test.ts +3 -37
- package/src/__tests__/skills-install-extract.test.ts +93 -0
- package/src/__tests__/skillssh-registry.test.ts +451 -0
- package/src/__tests__/trust-store.test.ts +15 -0
- package/src/__tests__/voice-invite-redemption.test.ts +32 -1
- package/src/agent/ax-tree-compaction.test.ts +51 -0
- package/src/agent/loop.ts +39 -12
- package/src/approvals/AGENTS.md +1 -1
- package/src/approvals/guardian-request-resolvers.ts +14 -2
- package/src/bundler/compiler-tools.ts +66 -2
- package/src/calls/call-domain.ts +132 -0
- package/src/calls/call-store.ts +6 -0
- package/src/calls/relay-server.ts +43 -5
- package/src/calls/relay-setup-router.ts +17 -1
- package/src/calls/twilio-config.ts +1 -1
- package/src/calls/types.ts +3 -1
- package/src/cli/commands/doctor.ts +4 -3
- package/src/cli/commands/mcp.ts +46 -59
- package/src/cli/commands/memory.ts +16 -165
- package/src/cli/commands/oauth/apps.ts +31 -2
- package/src/cli/commands/oauth/connections.ts +431 -97
- package/src/cli/commands/oauth/providers.ts +15 -1
- package/src/cli/commands/sessions.ts +5 -2
- package/src/cli/commands/skills.ts +173 -1
- package/src/cli/http-client.ts +0 -20
- package/src/cli/main-screen.tsx +2 -2
- package/src/cli/program.ts +5 -6
- package/src/cli.ts +4 -10
- package/src/config/bundled-skills/computer-use/TOOLS.json +1 -1
- package/src/config/bundled-skills/computer-use/tools/computer-use-observe.ts +12 -0
- package/src/config/bundled-tool-registry.ts +2 -5
- package/src/config/schema.ts +1 -12
- package/src/config/schemas/memory-lifecycle.ts +0 -9
- package/src/config/schemas/memory-processing.ts +0 -180
- package/src/config/schemas/memory-retrieval.ts +32 -104
- package/src/config/schemas/memory.ts +0 -10
- package/src/config/types.ts +0 -4
- package/src/context/window-manager.ts +4 -1
- package/src/daemon/config-watcher.ts +61 -3
- package/src/daemon/daemon-control.ts +1 -1
- package/src/daemon/date-context.ts +114 -31
- package/src/daemon/handlers/sessions.ts +18 -13
- package/src/daemon/handlers/skills.ts +20 -1
- package/src/daemon/history-repair.ts +72 -8
- package/src/daemon/host-cu-proxy.ts +55 -26
- package/src/daemon/lifecycle.ts +31 -3
- package/src/daemon/mcp-reload-service.ts +2 -2
- package/src/daemon/message-types/computer-use.ts +1 -12
- package/src/daemon/message-types/memory.ts +4 -16
- package/src/daemon/message-types/messages.ts +1 -0
- package/src/daemon/message-types/sessions.ts +4 -0
- package/src/daemon/server.ts +12 -1
- package/src/daemon/session-agent-loop-handlers.ts +38 -0
- package/src/daemon/session-agent-loop.ts +334 -48
- package/src/daemon/session-error.ts +89 -6
- package/src/daemon/session-history.ts +17 -7
- package/src/daemon/session-media-retry.ts +6 -2
- package/src/daemon/session-memory.ts +69 -149
- package/src/daemon/session-process.ts +10 -1
- package/src/daemon/session-runtime-assembly.ts +49 -19
- package/src/daemon/session-surfaces.ts +4 -1
- package/src/daemon/session-tool-setup.ts +7 -1
- package/src/daemon/session.ts +12 -2
- package/src/instrument.ts +61 -1
- package/src/memory/admin.ts +2 -191
- package/src/memory/canonical-guardian-store.ts +38 -2
- package/src/memory/conversation-crud.ts +0 -33
- package/src/memory/conversation-queries.ts +22 -3
- package/src/memory/db-init.ts +28 -0
- package/src/memory/embedding-backend.ts +84 -8
- package/src/memory/embedding-types.ts +9 -1
- package/src/memory/indexer.ts +7 -46
- package/src/memory/items-extractor.ts +274 -76
- package/src/memory/job-handlers/backfill.ts +2 -127
- package/src/memory/job-handlers/cleanup.ts +2 -16
- package/src/memory/job-handlers/extraction.ts +2 -138
- package/src/memory/job-handlers/index-maintenance.ts +1 -6
- package/src/memory/job-handlers/summarization.ts +3 -148
- package/src/memory/job-utils.ts +21 -59
- package/src/memory/jobs-store.ts +1 -159
- package/src/memory/jobs-worker.ts +9 -52
- package/src/memory/migrations/104-core-indexes.ts +3 -3
- package/src/memory/migrations/149-oauth-tables.ts +2 -0
- package/src/memory/migrations/150-oauth-apps-client-secret-path.ts +98 -0
- package/src/memory/migrations/151-oauth-providers-ping-url.ts +11 -0
- package/src/memory/migrations/152-memory-item-supersession.ts +44 -0
- package/src/memory/migrations/153-drop-entity-tables.ts +15 -0
- package/src/memory/migrations/154-drop-fts.ts +20 -0
- package/src/memory/migrations/155-drop-conflicts.ts +7 -0
- package/src/memory/migrations/156-call-session-invite-metadata.ts +24 -0
- package/src/memory/migrations/index.ts +7 -0
- package/src/memory/qdrant-client.ts +148 -51
- package/src/memory/raw-query.ts +1 -1
- package/src/memory/retriever.test.ts +294 -273
- package/src/memory/retriever.ts +421 -645
- package/src/memory/schema/calls.ts +2 -0
- package/src/memory/schema/memory-core.ts +3 -48
- package/src/memory/schema/oauth.ts +2 -0
- package/src/memory/search/formatting.ts +263 -176
- package/src/memory/search/lexical.ts +1 -254
- package/src/memory/search/ranking.ts +0 -455
- package/src/memory/search/semantic.ts +100 -14
- package/src/memory/search/staleness.ts +47 -0
- package/src/memory/search/tier-classifier.ts +21 -0
- package/src/memory/search/types.ts +15 -77
- package/src/memory/task-memory-cleanup.ts +4 -6
- package/src/messaging/providers/gmail/mime-builder.ts +17 -7
- package/src/oauth/byo-connection.test.ts +8 -1
- package/src/oauth/oauth-store.ts +113 -27
- package/src/oauth/seed-providers.ts +6 -0
- package/src/oauth/token-persistence.ts +11 -3
- package/src/permissions/defaults.ts +1 -0
- package/src/permissions/trust-store.ts +23 -1
- package/src/playbooks/playbook-compiler.ts +1 -1
- package/src/prompts/system-prompt.ts +18 -2
- package/src/providers/anthropic/client.ts +56 -126
- package/src/providers/types.ts +7 -1
- package/src/runtime/AGENTS.md +9 -0
- package/src/runtime/auth/route-policy.ts +6 -3
- package/src/runtime/guardian-reply-router.ts +24 -22
- package/src/runtime/http-server.ts +2 -2
- package/src/runtime/invite-redemption-service.ts +19 -1
- package/src/runtime/invite-service.ts +25 -0
- package/src/runtime/pending-interactions.ts +2 -2
- package/src/runtime/routes/brain-graph-routes.ts +10 -90
- package/src/runtime/routes/conversation-routes.ts +9 -1
- package/src/runtime/routes/inbound-stages/acl-enforcement.ts +21 -12
- package/src/runtime/routes/memory-item-routes.test.ts +754 -0
- package/src/runtime/routes/memory-item-routes.ts +503 -0
- package/src/runtime/routes/session-management-routes.ts +3 -3
- package/src/runtime/routes/settings-routes.ts +2 -2
- package/src/runtime/routes/trust-rules-routes.ts +14 -0
- package/src/runtime/routes/workspace-routes.ts +2 -1
- package/src/security/keychain-broker-client.ts +17 -4
- package/src/security/secure-keys.ts +25 -3
- package/src/security/token-manager.ts +36 -36
- package/src/skills/catalog-install.ts +74 -18
- package/src/skills/skillssh-registry.ts +503 -0
- package/src/tools/assets/search.ts +5 -1
- package/src/tools/computer-use/definitions.ts +0 -10
- package/src/tools/computer-use/registry.ts +1 -1
- package/src/tools/credentials/vault.ts +1 -3
- package/src/tools/memory/definitions.ts +4 -13
- package/src/tools/memory/handlers.test.ts +83 -103
- package/src/tools/memory/handlers.ts +50 -85
- package/src/tools/schedule/create.ts +8 -1
- package/src/tools/schedule/update.ts +8 -1
- package/src/tools/skills/load.ts +25 -2
- package/src/__tests__/clarification-resolver.test.ts +0 -193
- package/src/__tests__/conflict-intent-tokenization.test.ts +0 -160
- package/src/__tests__/conflict-policy.test.ts +0 -269
- package/src/__tests__/conflict-store.test.ts +0 -372
- package/src/__tests__/contradiction-checker.test.ts +0 -361
- package/src/__tests__/entity-extractor.test.ts +0 -211
- package/src/__tests__/entity-search.test.ts +0 -1117
- package/src/__tests__/profile-compiler.test.ts +0 -392
- package/src/__tests__/session-conflict-gate.test.ts +0 -1228
- package/src/__tests__/session-profile-injection.test.ts +0 -557
- package/src/config/bundled-skills/knowledge-graph/SKILL.md +0 -25
- package/src/config/bundled-skills/knowledge-graph/TOOLS.json +0 -66
- package/src/config/bundled-skills/knowledge-graph/tools/graph-query.ts +0 -211
- package/src/daemon/session-conflict-gate.ts +0 -167
- package/src/daemon/session-dynamic-profile.ts +0 -77
- package/src/memory/clarification-resolver.ts +0 -417
- package/src/memory/conflict-intent.ts +0 -205
- package/src/memory/conflict-policy.ts +0 -127
- package/src/memory/conflict-store.ts +0 -410
- package/src/memory/contradiction-checker.ts +0 -508
- package/src/memory/entity-extractor.ts +0 -535
- package/src/memory/format-recall.ts +0 -47
- package/src/memory/fts-reconciler.ts +0 -165
- package/src/memory/job-handlers/conflict.ts +0 -200
- package/src/memory/profile-compiler.ts +0 -195
- package/src/memory/recall-cache.ts +0 -117
- package/src/memory/search/entity.ts +0 -535
- package/src/memory/search/query-expansion.test.ts +0 -70
- package/src/memory/search/query-expansion.ts +0 -118
- package/src/runtime/routes/mcp-routes.ts +0 -20
|
@@ -1,22 +1,5 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
|
|
3
|
-
export const MemoryRerankingConfigSchema = z.object({
|
|
4
|
-
enabled: z
|
|
5
|
-
.boolean({ error: "memory.retrieval.reranking.enabled must be a boolean" })
|
|
6
|
-
.default(false),
|
|
7
|
-
modelIntent: z
|
|
8
|
-
.enum(["latency-optimized", "quality-optimized", "vision-optimized"], {
|
|
9
|
-
error:
|
|
10
|
-
"memory.retrieval.reranking.modelIntent must be a valid model intent",
|
|
11
|
-
})
|
|
12
|
-
.default("latency-optimized"),
|
|
13
|
-
topK: z
|
|
14
|
-
.number({ error: "memory.retrieval.reranking.topK must be a number" })
|
|
15
|
-
.int("memory.retrieval.reranking.topK must be an integer")
|
|
16
|
-
.positive("memory.retrieval.reranking.topK must be a positive integer")
|
|
17
|
-
.default(20),
|
|
18
|
-
});
|
|
19
|
-
|
|
20
3
|
export const MemoryDynamicBudgetConfigSchema = z.object({
|
|
21
4
|
enabled: z
|
|
22
5
|
.boolean({
|
|
@@ -55,49 +38,6 @@ export const MemoryDynamicBudgetConfigSchema = z.object({
|
|
|
55
38
|
.default(10000),
|
|
56
39
|
});
|
|
57
40
|
|
|
58
|
-
export const MemoryEarlyTerminationConfigSchema = z.object({
|
|
59
|
-
enabled: z
|
|
60
|
-
.boolean({
|
|
61
|
-
error: "memory.retrieval.earlyTermination.enabled must be a boolean",
|
|
62
|
-
})
|
|
63
|
-
.default(true),
|
|
64
|
-
minCandidates: z
|
|
65
|
-
.number({
|
|
66
|
-
error: "memory.retrieval.earlyTermination.minCandidates must be a number",
|
|
67
|
-
})
|
|
68
|
-
.int("memory.retrieval.earlyTermination.minCandidates must be an integer")
|
|
69
|
-
.positive(
|
|
70
|
-
"memory.retrieval.earlyTermination.minCandidates must be a positive integer",
|
|
71
|
-
)
|
|
72
|
-
.default(20),
|
|
73
|
-
minHighConfidence: z
|
|
74
|
-
.number({
|
|
75
|
-
error:
|
|
76
|
-
"memory.retrieval.earlyTermination.minHighConfidence must be a number",
|
|
77
|
-
})
|
|
78
|
-
.int(
|
|
79
|
-
"memory.retrieval.earlyTermination.minHighConfidence must be an integer",
|
|
80
|
-
)
|
|
81
|
-
.positive(
|
|
82
|
-
"memory.retrieval.earlyTermination.minHighConfidence must be a positive integer",
|
|
83
|
-
)
|
|
84
|
-
.default(10),
|
|
85
|
-
confidenceThreshold: z
|
|
86
|
-
.number({
|
|
87
|
-
error:
|
|
88
|
-
"memory.retrieval.earlyTermination.confidenceThreshold must be a number",
|
|
89
|
-
})
|
|
90
|
-
.min(
|
|
91
|
-
0,
|
|
92
|
-
"memory.retrieval.earlyTermination.confidenceThreshold must be >= 0",
|
|
93
|
-
)
|
|
94
|
-
.max(
|
|
95
|
-
1,
|
|
96
|
-
"memory.retrieval.earlyTermination.confidenceThreshold must be <= 1",
|
|
97
|
-
)
|
|
98
|
-
.default(0.7),
|
|
99
|
-
});
|
|
100
|
-
|
|
101
41
|
/**
|
|
102
42
|
* Per-kind freshness windows (in days). Items older than their window
|
|
103
43
|
* (based on lastSeenAt) are down-ranked unless recently reinforced.
|
|
@@ -109,12 +49,13 @@ const MemoryFreshnessConfigSchema = z.object({
|
|
|
109
49
|
.default(true),
|
|
110
50
|
maxAgeDays: z
|
|
111
51
|
.object({
|
|
112
|
-
|
|
52
|
+
identity: z
|
|
113
53
|
.number({
|
|
114
|
-
error:
|
|
54
|
+
error:
|
|
55
|
+
"memory.retrieval.freshness.maxAgeDays.identity must be a number",
|
|
115
56
|
})
|
|
116
57
|
.nonnegative(
|
|
117
|
-
"memory.retrieval.freshness.maxAgeDays.
|
|
58
|
+
"memory.retrieval.freshness.maxAgeDays.identity must be non-negative",
|
|
118
59
|
)
|
|
119
60
|
.default(0),
|
|
120
61
|
preference: z
|
|
@@ -126,34 +67,50 @@ const MemoryFreshnessConfigSchema = z.object({
|
|
|
126
67
|
"memory.retrieval.freshness.maxAgeDays.preference must be non-negative",
|
|
127
68
|
)
|
|
128
69
|
.default(0),
|
|
129
|
-
|
|
70
|
+
project: z
|
|
130
71
|
.number({
|
|
131
72
|
error:
|
|
132
|
-
"memory.retrieval.freshness.maxAgeDays.
|
|
73
|
+
"memory.retrieval.freshness.maxAgeDays.project must be a number",
|
|
133
74
|
})
|
|
134
75
|
.nonnegative(
|
|
135
|
-
"memory.retrieval.freshness.maxAgeDays.
|
|
76
|
+
"memory.retrieval.freshness.maxAgeDays.project must be non-negative",
|
|
136
77
|
)
|
|
137
|
-
.default(
|
|
138
|
-
|
|
78
|
+
.default(30),
|
|
79
|
+
decision: z
|
|
139
80
|
.number({
|
|
140
|
-
error:
|
|
81
|
+
error:
|
|
82
|
+
"memory.retrieval.freshness.maxAgeDays.decision must be a number",
|
|
141
83
|
})
|
|
142
84
|
.nonnegative(
|
|
143
|
-
"memory.retrieval.freshness.maxAgeDays.
|
|
85
|
+
"memory.retrieval.freshness.maxAgeDays.decision must be non-negative",
|
|
144
86
|
)
|
|
145
87
|
.default(30),
|
|
146
|
-
|
|
88
|
+
constraint: z
|
|
147
89
|
.number({
|
|
148
90
|
error:
|
|
149
|
-
"memory.retrieval.freshness.maxAgeDays.
|
|
91
|
+
"memory.retrieval.freshness.maxAgeDays.constraint must be a number",
|
|
92
|
+
})
|
|
93
|
+
.nonnegative(
|
|
94
|
+
"memory.retrieval.freshness.maxAgeDays.constraint must be non-negative",
|
|
95
|
+
)
|
|
96
|
+
.default(90),
|
|
97
|
+
event: z
|
|
98
|
+
.number({
|
|
99
|
+
error: "memory.retrieval.freshness.maxAgeDays.event must be a number",
|
|
150
100
|
})
|
|
151
101
|
.nonnegative(
|
|
152
|
-
"memory.retrieval.freshness.maxAgeDays.
|
|
102
|
+
"memory.retrieval.freshness.maxAgeDays.event must be non-negative",
|
|
153
103
|
)
|
|
154
|
-
.default(
|
|
104
|
+
.default(30),
|
|
155
105
|
})
|
|
156
|
-
.default({
|
|
106
|
+
.default({
|
|
107
|
+
identity: 0,
|
|
108
|
+
preference: 0,
|
|
109
|
+
project: 30,
|
|
110
|
+
decision: 30,
|
|
111
|
+
constraint: 90,
|
|
112
|
+
event: 30,
|
|
113
|
+
}),
|
|
157
114
|
staleDecay: z
|
|
158
115
|
.number({ error: "memory.retrieval.freshness.staleDecay must be a number" })
|
|
159
116
|
.min(0, "memory.retrieval.freshness.staleDecay must be >= 0")
|
|
@@ -171,36 +128,11 @@ const MemoryFreshnessConfigSchema = z.object({
|
|
|
171
128
|
});
|
|
172
129
|
|
|
173
130
|
export const MemoryRetrievalConfigSchema = z.object({
|
|
174
|
-
lexicalTopK: z
|
|
175
|
-
.number({ error: "memory.retrieval.lexicalTopK must be a number" })
|
|
176
|
-
.int("memory.retrieval.lexicalTopK must be an integer")
|
|
177
|
-
.positive("memory.retrieval.lexicalTopK must be a positive integer")
|
|
178
|
-
.default(80),
|
|
179
|
-
semanticTopK: z
|
|
180
|
-
.number({ error: "memory.retrieval.semanticTopK must be a number" })
|
|
181
|
-
.int("memory.retrieval.semanticTopK must be an integer")
|
|
182
|
-
.positive("memory.retrieval.semanticTopK must be a positive integer")
|
|
183
|
-
.default(40),
|
|
184
131
|
maxInjectTokens: z
|
|
185
132
|
.number({ error: "memory.retrieval.maxInjectTokens must be a number" })
|
|
186
133
|
.int("memory.retrieval.maxInjectTokens must be an integer")
|
|
187
134
|
.positive("memory.retrieval.maxInjectTokens must be a positive integer")
|
|
188
135
|
.default(10000),
|
|
189
|
-
injectionFormat: z
|
|
190
|
-
.enum(["markdown", "structured_v1"], {
|
|
191
|
-
error:
|
|
192
|
-
'memory.retrieval.injectionFormat must be "markdown" or "structured_v1"',
|
|
193
|
-
})
|
|
194
|
-
.default("markdown"),
|
|
195
|
-
injectionStrategy: z
|
|
196
|
-
.enum(["prepend_user_block", "separate_context_message"], {
|
|
197
|
-
error:
|
|
198
|
-
'memory.retrieval.injectionStrategy must be "prepend_user_block" or "separate_context_message"',
|
|
199
|
-
})
|
|
200
|
-
.default("prepend_user_block"),
|
|
201
|
-
reranking: MemoryRerankingConfigSchema.default(
|
|
202
|
-
MemoryRerankingConfigSchema.parse({}),
|
|
203
|
-
),
|
|
204
136
|
freshness: MemoryFreshnessConfigSchema.default(
|
|
205
137
|
MemoryFreshnessConfigSchema.parse({}),
|
|
206
138
|
),
|
|
@@ -213,10 +145,6 @@ export const MemoryRetrievalConfigSchema = z.object({
|
|
|
213
145
|
dynamicBudget: MemoryDynamicBudgetConfigSchema.default(
|
|
214
146
|
MemoryDynamicBudgetConfigSchema.parse({}),
|
|
215
147
|
),
|
|
216
|
-
earlyTermination: MemoryEarlyTerminationConfigSchema.default(
|
|
217
|
-
MemoryEarlyTerminationConfigSchema.parse({}),
|
|
218
|
-
),
|
|
219
148
|
});
|
|
220
149
|
|
|
221
|
-
export type MemoryRerankingConfig = z.infer<typeof MemoryRerankingConfigSchema>;
|
|
222
150
|
export type MemoryRetrievalConfig = z.infer<typeof MemoryRetrievalConfigSchema>;
|
|
@@ -6,10 +6,7 @@ import {
|
|
|
6
6
|
MemoryRetentionConfigSchema,
|
|
7
7
|
} from "./memory-lifecycle.js";
|
|
8
8
|
import {
|
|
9
|
-
MemoryConflictsConfigSchema,
|
|
10
|
-
MemoryEntityConfigSchema,
|
|
11
9
|
MemoryExtractionConfigSchema,
|
|
12
|
-
MemoryProfileConfigSchema,
|
|
13
10
|
MemorySummarizationConfigSchema,
|
|
14
11
|
} from "./memory-processing.js";
|
|
15
12
|
import { MemoryRetrievalConfigSchema } from "./memory-retrieval.js";
|
|
@@ -46,13 +43,6 @@ export const MemoryConfigSchema = z.object({
|
|
|
46
43
|
summarization: MemorySummarizationConfigSchema.default(
|
|
47
44
|
MemorySummarizationConfigSchema.parse({}),
|
|
48
45
|
),
|
|
49
|
-
entity: MemoryEntityConfigSchema.default(MemoryEntityConfigSchema.parse({})),
|
|
50
|
-
conflicts: MemoryConflictsConfigSchema.default(
|
|
51
|
-
MemoryConflictsConfigSchema.parse({}),
|
|
52
|
-
),
|
|
53
|
-
profile: MemoryProfileConfigSchema.default(
|
|
54
|
-
MemoryProfileConfigSchema.parse({}),
|
|
55
|
-
),
|
|
56
46
|
});
|
|
57
47
|
|
|
58
48
|
export type MemoryConfig = z.infer<typeof MemoryConfigSchema>;
|
package/src/config/types.ts
CHANGED
|
@@ -12,13 +12,9 @@ export type {
|
|
|
12
12
|
IngressConfig,
|
|
13
13
|
LogFileConfig,
|
|
14
14
|
MemoryConfig,
|
|
15
|
-
MemoryConflictsConfig,
|
|
16
15
|
MemoryEmbeddingsConfig,
|
|
17
|
-
MemoryEntityConfig,
|
|
18
16
|
MemoryExtractionConfig,
|
|
19
17
|
MemoryJobsConfig,
|
|
20
|
-
MemoryProfileConfig,
|
|
21
|
-
MemoryRerankingConfig,
|
|
22
18
|
MemoryRetentionConfig,
|
|
23
19
|
MemoryRetrievalConfig,
|
|
24
20
|
MemorySegmentationConfig,
|
|
@@ -671,7 +671,10 @@ function countPersistedMessages(messages: Message[]): number {
|
|
|
671
671
|
function isToolResultOnly(message: Message): boolean {
|
|
672
672
|
return (
|
|
673
673
|
message.content.length > 0 &&
|
|
674
|
-
message.content.every(
|
|
674
|
+
message.content.every(
|
|
675
|
+
(block) =>
|
|
676
|
+
block.type === "tool_result" || block.type === "web_search_tool_result",
|
|
677
|
+
)
|
|
675
678
|
);
|
|
676
679
|
}
|
|
677
680
|
|
|
@@ -3,7 +3,13 @@
|
|
|
3
3
|
* Watches workspace files (config, prompts), protected directory
|
|
4
4
|
* (trust rules, secret allowlist), and skills directories for changes.
|
|
5
5
|
*/
|
|
6
|
-
import {
|
|
6
|
+
import {
|
|
7
|
+
existsSync,
|
|
8
|
+
type FSWatcher,
|
|
9
|
+
mkdirSync,
|
|
10
|
+
readdirSync,
|
|
11
|
+
watch,
|
|
12
|
+
} from "node:fs";
|
|
7
13
|
import { join } from "node:path";
|
|
8
14
|
|
|
9
15
|
import { getConfig, invalidateConfigCache } from "../config/loader.js";
|
|
@@ -98,8 +104,14 @@ export class ConfigWatcher {
|
|
|
98
104
|
* Start all file watchers. `onSessionEvict` is called when watched
|
|
99
105
|
* files change and sessions need to be evicted for reload.
|
|
100
106
|
* `onIdentityChanged` is called when IDENTITY.md changes on disk.
|
|
107
|
+
* `onMcpReload` is called when the MCP section of config.json changes
|
|
108
|
+
* or when a signal file appears in the workspace `signals/` directory.
|
|
101
109
|
*/
|
|
102
|
-
start(
|
|
110
|
+
start(
|
|
111
|
+
onSessionEvict: () => void,
|
|
112
|
+
onIdentityChanged?: () => void,
|
|
113
|
+
onMcpReload?: () => void,
|
|
114
|
+
): void {
|
|
103
115
|
const workspaceDir = getWorkspaceDir();
|
|
104
116
|
const protectedDir = join(getRootDir(), "protected");
|
|
105
117
|
|
|
@@ -107,8 +119,17 @@ export class ConfigWatcher {
|
|
|
107
119
|
"config.json": () => {
|
|
108
120
|
if (this.suppressReload) return;
|
|
109
121
|
try {
|
|
122
|
+
const prevConfig = getConfig();
|
|
123
|
+
const prevMcpFingerprint = JSON.stringify(prevConfig.mcp ?? {});
|
|
110
124
|
const changed = this.refreshConfigFromSources();
|
|
111
|
-
if (changed)
|
|
125
|
+
if (changed) {
|
|
126
|
+
onSessionEvict();
|
|
127
|
+
const newConfig = getConfig();
|
|
128
|
+
const newMcpFingerprint = JSON.stringify(newConfig.mcp ?? {});
|
|
129
|
+
if (newMcpFingerprint !== prevMcpFingerprint) {
|
|
130
|
+
onMcpReload?.();
|
|
131
|
+
}
|
|
132
|
+
}
|
|
112
133
|
} catch (err) {
|
|
113
134
|
log.error(
|
|
114
135
|
{ err, configPath: join(workspaceDir, "config.json") },
|
|
@@ -185,6 +206,7 @@ export class ConfigWatcher {
|
|
|
185
206
|
);
|
|
186
207
|
}
|
|
187
208
|
|
|
209
|
+
this.startSignalsWatcher(onMcpReload);
|
|
188
210
|
this.startSkillsWatchers(onSessionEvict);
|
|
189
211
|
}
|
|
190
212
|
|
|
@@ -196,6 +218,42 @@ export class ConfigWatcher {
|
|
|
196
218
|
this.watchers = [];
|
|
197
219
|
}
|
|
198
220
|
|
|
221
|
+
private startSignalsWatcher(onMcpReload?: () => void): void {
|
|
222
|
+
const signalsDir = join(getWorkspaceDir(), "signals");
|
|
223
|
+
try {
|
|
224
|
+
if (!existsSync(signalsDir)) {
|
|
225
|
+
mkdirSync(signalsDir, { recursive: true });
|
|
226
|
+
}
|
|
227
|
+
} catch {
|
|
228
|
+
// If we can't create it, watching will also fail — handled below.
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
const signalHandlers: Record<string, () => void> = {
|
|
232
|
+
"mcp-reload": () => {
|
|
233
|
+
onMcpReload?.();
|
|
234
|
+
},
|
|
235
|
+
};
|
|
236
|
+
|
|
237
|
+
try {
|
|
238
|
+
const watcher = watch(signalsDir, (_eventType, filename) => {
|
|
239
|
+
if (!filename) return;
|
|
240
|
+
const file = String(filename);
|
|
241
|
+
if (!signalHandlers[file]) return;
|
|
242
|
+
this.debounceTimers.schedule(`signal:${file}`, () => {
|
|
243
|
+
log.info({ file }, "Signal file detected");
|
|
244
|
+
signalHandlers[file]();
|
|
245
|
+
});
|
|
246
|
+
});
|
|
247
|
+
this.watchers.push(watcher);
|
|
248
|
+
log.info({ dir: signalsDir }, "Watching signals directory");
|
|
249
|
+
} catch (err) {
|
|
250
|
+
log.warn(
|
|
251
|
+
{ err, dir: signalsDir },
|
|
252
|
+
"Failed to watch signals directory. Signal-based reload will be unavailable.",
|
|
253
|
+
);
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
199
257
|
private startSkillsWatchers(onSessionEvict: () => void): void {
|
|
200
258
|
const skillsDir = getWorkspaceSkillsDir();
|
|
201
259
|
if (!existsSync(skillsDir)) return;
|
|
@@ -143,7 +143,7 @@ function healthCheckHost(host: string): string {
|
|
|
143
143
|
/** Hit the daemon's HTTP /healthz endpoint. Returns true if it responds
|
|
144
144
|
* with HTTP 200 within the timeout — false on connection refused, timeout,
|
|
145
145
|
* or any other error. */
|
|
146
|
-
async function isHttpHealthy(): Promise<boolean> {
|
|
146
|
+
export async function isHttpHealthy(): Promise<boolean> {
|
|
147
147
|
const host = healthCheckHost(getRuntimeHttpHost());
|
|
148
148
|
const port = getRuntimeHttpPort();
|
|
149
149
|
try {
|
|
@@ -33,10 +33,6 @@ const WEEKDAY_NAMES = [
|
|
|
33
33
|
"Friday",
|
|
34
34
|
"Saturday",
|
|
35
35
|
] as const;
|
|
36
|
-
const TIMEZONE_SUBJECT_LINE_RE = /^\s*-\s*time\s*zone\s*:\s*(.+)$/i;
|
|
37
|
-
const TIMEZONE_SUBJECT_COMPACT_RE = /^\s*-\s*timezone\s*:\s*(.+)$/i;
|
|
38
|
-
const TIMEZONE_TOKEN_RE =
|
|
39
|
-
/\b(?:[A-Za-z][A-Za-z0-9_+-]*(?:\/[A-Za-z0-9_+-]+)+|(?:UTC|GMT)(?:[+-]\d{1,2}(?::?\d{2})?)?)\b/gi;
|
|
40
36
|
const UTC_GMT_OFFSET_TOKEN_RE = /^(?:UTC|GMT)([+-])(\d{1,2})(?::?(\d{2}))?$/i;
|
|
41
37
|
|
|
42
38
|
function normalizeOffsetToken(offsetToken: string): string {
|
|
@@ -111,6 +107,17 @@ function canonicalizeTimeZone(timeZone: string): string | null {
|
|
|
111
107
|
return null;
|
|
112
108
|
}
|
|
113
109
|
}
|
|
110
|
+
// Check abbreviation mapping before Intl (many abbreviations are not recognized by Intl)
|
|
111
|
+
const abbrIana = TIMEZONE_ABBREVIATIONS[trimmed.toUpperCase()];
|
|
112
|
+
if (abbrIana) {
|
|
113
|
+
try {
|
|
114
|
+
return new Intl.DateTimeFormat("en-US", {
|
|
115
|
+
timeZone: abbrIana,
|
|
116
|
+
}).resolvedOptions().timeZone;
|
|
117
|
+
} catch {
|
|
118
|
+
return null;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
114
121
|
try {
|
|
115
122
|
return new Intl.DateTimeFormat("en-US", {
|
|
116
123
|
timeZone: trimmed,
|
|
@@ -120,47 +127,123 @@ function canonicalizeTimeZone(timeZone: string): string | null {
|
|
|
120
127
|
}
|
|
121
128
|
}
|
|
122
129
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
130
|
+
/**
|
|
131
|
+
* Common timezone abbreviation → IANA identifier mapping.
|
|
132
|
+
* Used as a fallback when `Intl.DateTimeFormat` does not recognize the abbreviation.
|
|
133
|
+
*/
|
|
134
|
+
const TIMEZONE_ABBREVIATIONS: Record<string, string> = {
|
|
135
|
+
// North America
|
|
136
|
+
PST: "America/Los_Angeles",
|
|
137
|
+
PDT: "America/Los_Angeles",
|
|
138
|
+
MST: "America/Denver",
|
|
139
|
+
MDT: "America/Denver",
|
|
140
|
+
CST: "America/Chicago",
|
|
141
|
+
CDT: "America/Chicago",
|
|
142
|
+
EST: "America/New_York",
|
|
143
|
+
EDT: "America/New_York",
|
|
144
|
+
AKST: "America/Anchorage",
|
|
145
|
+
AKDT: "America/Anchorage",
|
|
146
|
+
HST: "Pacific/Honolulu",
|
|
147
|
+
AST: "America/Puerto_Rico",
|
|
148
|
+
NST: "America/St_Johns",
|
|
149
|
+
NDT: "America/St_Johns",
|
|
150
|
+
// Europe
|
|
151
|
+
BST: "Europe/London",
|
|
152
|
+
CET: "Europe/Paris",
|
|
153
|
+
CEST: "Europe/Paris",
|
|
154
|
+
EET: "Europe/Athens",
|
|
155
|
+
EEST: "Europe/Athens",
|
|
156
|
+
WEST: "Europe/Lisbon",
|
|
157
|
+
MSK: "Europe/Moscow",
|
|
158
|
+
// Asia / Oceania
|
|
159
|
+
JST: "Asia/Tokyo",
|
|
160
|
+
KST: "Asia/Seoul",
|
|
161
|
+
HKT: "Asia/Hong_Kong",
|
|
162
|
+
SGT: "Asia/Singapore",
|
|
163
|
+
WIB: "Asia/Jakarta",
|
|
164
|
+
PHT: "Asia/Manila",
|
|
165
|
+
PKT: "Asia/Karachi",
|
|
166
|
+
NPT: "Asia/Kathmandu",
|
|
167
|
+
AEST: "Australia/Sydney",
|
|
168
|
+
AEDT: "Australia/Sydney",
|
|
169
|
+
ACST: "Australia/Adelaide",
|
|
170
|
+
ACDT: "Australia/Adelaide",
|
|
171
|
+
AWST: "Australia/Perth",
|
|
172
|
+
NZST: "Pacific/Auckland",
|
|
173
|
+
NZDT: "Pacific/Auckland",
|
|
174
|
+
// South America
|
|
175
|
+
BRT: "America/Sao_Paulo",
|
|
176
|
+
};
|
|
131
177
|
|
|
132
178
|
/**
|
|
133
|
-
*
|
|
179
|
+
* Regex matching IANA timezone identifiers (e.g. "America/New_York"),
|
|
180
|
+
* UTC/GMT offset tokens (e.g. "UTC+5", "GMT-8:30"), and common
|
|
181
|
+
* timezone abbreviations (e.g. "PST", "EST", "JST").
|
|
134
182
|
*
|
|
135
|
-
*
|
|
136
|
-
* full profile body for valid IANA timezone identifiers.
|
|
183
|
+
* Abbreviation alternation is built from `TIMEZONE_ABBREVIATIONS` keys.
|
|
137
184
|
*/
|
|
138
|
-
|
|
139
|
-
|
|
185
|
+
const TIMEZONE_ABBR_ALTERNATION = Object.keys(TIMEZONE_ABBREVIATIONS).join("|");
|
|
186
|
+
const TIMEZONE_TOKEN_RE = new RegExp(
|
|
187
|
+
`\\b(?:[A-Za-z][A-Za-z0-9_+-]*(?:/[A-Za-z0-9_+-]+)+|(?:UTC|GMT)(?:[+-]\\d{1,2}(?::?\\d{2})?)?|(?:${TIMEZONE_ABBR_ALTERNATION}))\\b`,
|
|
188
|
+
"gi",
|
|
189
|
+
);
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Extract the user's timezone from V2 memory recall injected text.
|
|
193
|
+
*
|
|
194
|
+
* Scans the `<user_identity>` section (if present) for lines containing
|
|
195
|
+
* "timezone" and tries to resolve an IANA identifier. Falls back to
|
|
196
|
+
* scanning the full text body.
|
|
197
|
+
*/
|
|
198
|
+
export function extractUserTimeZoneFromRecall(
|
|
199
|
+
injectedText: string,
|
|
140
200
|
): string | null {
|
|
141
|
-
|
|
142
|
-
if (trimmed.length === 0) return null;
|
|
201
|
+
if (!injectedText || injectedText.trim().length === 0) return null;
|
|
143
202
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
203
|
+
// Prefer lines inside <user_identity> that mention "timezone"
|
|
204
|
+
const identityMatch = injectedText.match(
|
|
205
|
+
/<user_identity>([\s\S]*?)<\/user_identity>/,
|
|
206
|
+
);
|
|
207
|
+
if (identityMatch) {
|
|
208
|
+
const identityBlock = identityMatch[1];
|
|
209
|
+
for (const line of identityBlock.split("\n")) {
|
|
210
|
+
if (/time\s*zone/i.test(line)) {
|
|
211
|
+
for (const token of extractTimeZoneCandidates(line)) {
|
|
212
|
+
const canonical = canonicalizeTimeZone(token);
|
|
213
|
+
if (canonical) return canonical;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
151
216
|
}
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
for (const text of candidateTexts) {
|
|
156
|
-
for (const token of extractTimeZoneCandidates(text)) {
|
|
217
|
+
// Scan full identity block for any timezone token
|
|
218
|
+
for (const token of extractTimeZoneCandidates(identityBlock)) {
|
|
157
219
|
const canonical = canonicalizeTimeZone(token);
|
|
158
220
|
if (canonical) return canonical;
|
|
159
221
|
}
|
|
160
222
|
}
|
|
223
|
+
|
|
224
|
+
// Fallback: scan entire injected text for timezone tokens in
|
|
225
|
+
// lines that mention "timezone"
|
|
226
|
+
for (const line of injectedText.split("\n")) {
|
|
227
|
+
if (/time\s*zone/i.test(line)) {
|
|
228
|
+
for (const token of extractTimeZoneCandidates(line)) {
|
|
229
|
+
const canonical = canonicalizeTimeZone(token);
|
|
230
|
+
if (canonical) return canonical;
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
161
235
|
return null;
|
|
162
236
|
}
|
|
163
237
|
|
|
238
|
+
function extractTimeZoneCandidates(text: string): string[] {
|
|
239
|
+
const matches = (text.match(TIMEZONE_TOKEN_RE) ?? [])
|
|
240
|
+
.map((token) => token.trim())
|
|
241
|
+
.filter((token) => token.length > 0);
|
|
242
|
+
const ianaTokens = matches.filter((token) => token.includes("/"));
|
|
243
|
+
const offsetTokens = matches.filter((token) => !token.includes("/"));
|
|
244
|
+
return [...ianaTokens, ...offsetTokens];
|
|
245
|
+
}
|
|
246
|
+
|
|
164
247
|
/**
|
|
165
248
|
* Get the local date parts for a given instant in the specified timezone.
|
|
166
249
|
*/
|
|
@@ -421,8 +421,11 @@ export async function handleSessionCreate(
|
|
|
421
421
|
pendingInteractions.resolve(requestId);
|
|
422
422
|
});
|
|
423
423
|
session.setHostFileProxy(fileProxy);
|
|
424
|
-
const cuProxy = new HostCuProxy(sendEvent)
|
|
424
|
+
const cuProxy = new HostCuProxy(sendEvent, (requestId) => {
|
|
425
|
+
pendingInteractions.resolve(requestId);
|
|
426
|
+
});
|
|
425
427
|
session.setHostCuProxy(cuProxy);
|
|
428
|
+
session.addPreactivatedSkillId("computer-use");
|
|
426
429
|
}
|
|
427
430
|
session.updateClient(sendEvent, false);
|
|
428
431
|
session
|
|
@@ -575,23 +578,24 @@ export function handleCancel(msg: CancelRequest, ctx: HandlerContext): void {
|
|
|
575
578
|
}
|
|
576
579
|
|
|
577
580
|
/**
|
|
578
|
-
* Undo the last message in a session. Returns the removed count, or null if
|
|
581
|
+
* Undo the last message in a session. Returns the removed count, or null if
|
|
582
|
+
* the conversation does not exist. Restores evicted sessions from the database.
|
|
579
583
|
*/
|
|
580
|
-
export function undoLastMessage(
|
|
584
|
+
export async function undoLastMessage(
|
|
581
585
|
sessionId: string,
|
|
582
586
|
ctx: HandlerContext,
|
|
583
|
-
): { removedCount: number } | null {
|
|
584
|
-
|
|
585
|
-
if (!session) {
|
|
587
|
+
): Promise<{ removedCount: number } | null> {
|
|
588
|
+
if (!getConversation(sessionId)) {
|
|
586
589
|
return null;
|
|
587
590
|
}
|
|
591
|
+
const session = await ctx.getOrCreateSession(sessionId);
|
|
588
592
|
ctx.touchSession(sessionId);
|
|
589
593
|
const removedCount = session.undo();
|
|
590
594
|
return { removedCount };
|
|
591
595
|
}
|
|
592
596
|
|
|
593
|
-
export function handleUndo(msg: UndoRequest, ctx: HandlerContext): void {
|
|
594
|
-
const result = undoLastMessage(msg.sessionId, ctx);
|
|
597
|
+
export async function handleUndo(msg: UndoRequest, ctx: HandlerContext): Promise<void> {
|
|
598
|
+
const result = await undoLastMessage(msg.sessionId, ctx);
|
|
595
599
|
if (!result) {
|
|
596
600
|
ctx.send({ type: "error", message: "No active session" });
|
|
597
601
|
return;
|
|
@@ -606,17 +610,18 @@ export function handleUndo(msg: UndoRequest, ctx: HandlerContext): void {
|
|
|
606
610
|
/**
|
|
607
611
|
* Regenerate the last assistant response for a session. The caller provides
|
|
608
612
|
* a `sendEvent` callback for delivering streaming events via HTTP/SSE.
|
|
609
|
-
* Returns null if the
|
|
613
|
+
* Returns null if the conversation does not exist. Restores evicted sessions
|
|
614
|
+
* from the database when needed. Throws on regeneration errors.
|
|
610
615
|
*/
|
|
611
616
|
export async function regenerateResponse(
|
|
612
617
|
sessionId: string,
|
|
613
618
|
ctx: HandlerContext,
|
|
614
619
|
sendEvent: (event: ServerMessage) => void,
|
|
615
620
|
): Promise<{ requestId: string } | null> {
|
|
616
|
-
|
|
617
|
-
if (!session) {
|
|
621
|
+
if (!getConversation(sessionId)) {
|
|
618
622
|
return null;
|
|
619
623
|
}
|
|
624
|
+
const session = await ctx.getOrCreateSession(sessionId);
|
|
620
625
|
ctx.touchSession(sessionId);
|
|
621
626
|
session.updateClient(sendEvent, false);
|
|
622
627
|
const requestId = uuid();
|
|
@@ -647,11 +652,11 @@ export async function handleRegenerate(
|
|
|
647
652
|
msg: RegenerateRequest,
|
|
648
653
|
ctx: HandlerContext,
|
|
649
654
|
): Promise<void> {
|
|
650
|
-
|
|
651
|
-
if (!session) {
|
|
655
|
+
if (!getConversation(msg.sessionId)) {
|
|
652
656
|
ctx.send({ type: "error", message: "No active session" });
|
|
653
657
|
return;
|
|
654
658
|
}
|
|
659
|
+
const session = await ctx.getOrCreateSession(msg.sessionId);
|
|
655
660
|
|
|
656
661
|
const regenerateChannel =
|
|
657
662
|
parseChannelId(session.getTurnChannelContext()?.assistantMessageChannel) ??
|
|
@@ -250,12 +250,20 @@ export interface SkillListItem {
|
|
|
250
250
|
provenance: SkillProvenance;
|
|
251
251
|
}
|
|
252
252
|
|
|
253
|
+
/** Sorting rank for provenance-based ordering: first-party first, local last. */
|
|
254
|
+
function provenanceSortRank(p: SkillProvenance): number {
|
|
255
|
+
if (p.kind === "first-party") return 0;
|
|
256
|
+
if (p.kind === "third-party" && p.provider) return 1;
|
|
257
|
+
if (p.kind === "third-party") return 2;
|
|
258
|
+
return 3; // local
|
|
259
|
+
}
|
|
260
|
+
|
|
253
261
|
export function listSkills(_ctx: SkillOperationContext): SkillListItem[] {
|
|
254
262
|
const config = getConfig();
|
|
255
263
|
const catalog = loadSkillCatalog();
|
|
256
264
|
const resolved = resolveSkillStates(catalog, config);
|
|
257
265
|
|
|
258
|
-
|
|
266
|
+
const items = resolved.map((r) => ({
|
|
259
267
|
id: r.summary.id,
|
|
260
268
|
name: r.summary.displayName,
|
|
261
269
|
description: r.summary.description,
|
|
@@ -272,6 +280,17 @@ export function listSkills(_ctx: SkillOperationContext): SkillListItem[] {
|
|
|
272
280
|
userInvocable: r.summary.userInvocable,
|
|
273
281
|
provenance: resolveProvenance(r.summary),
|
|
274
282
|
}));
|
|
283
|
+
|
|
284
|
+
// Sort: first-party > third-party with provider > third-party without > local,
|
|
285
|
+
// alphabetical by name within each tier.
|
|
286
|
+
items.sort((a, b) => {
|
|
287
|
+
const rankDiff =
|
|
288
|
+
provenanceSortRank(a.provenance) - provenanceSortRank(b.provenance);
|
|
289
|
+
if (rankDiff !== 0) return rankDiff;
|
|
290
|
+
return a.name.localeCompare(b.name);
|
|
291
|
+
});
|
|
292
|
+
|
|
293
|
+
return items;
|
|
275
294
|
}
|
|
276
295
|
|
|
277
296
|
export function enableSkill(
|