@vellumai/assistant 0.12.5 → 0.12.6-dev.202609292116.41f7caa
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/.env.example +2 -0
- package/ARCHITECTURE.md +26 -3
- package/docs/automatic-done.md +32 -0
- package/docs/desktop-browser-cli.md +3 -2
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/package.json +2 -1
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/__tests__/mcp-oauth.test.ts +53 -0
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/index.ts +1 -0
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/mcp-oauth.ts +60 -0
- package/node_modules/@vellumai/environments/src/shell.test.ts +13 -1
- package/node_modules/@vellumai/environments/src/shell.ts +22 -1
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/package.json +2 -1
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/__tests__/mcp-oauth.test.ts +53 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/index.ts +1 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/mcp-oauth.ts +60 -0
- package/node_modules/@vellumai/gateway-client/src/__tests__/status-contracts.test.ts +39 -0
- package/node_modules/@vellumai/gateway-client/src/gateway-ipc-contracts.ts +25 -0
- package/node_modules/@vellumai/service-contracts/package.json +2 -1
- package/node_modules/@vellumai/service-contracts/src/__tests__/mcp-oauth.test.ts +53 -0
- package/node_modules/@vellumai/service-contracts/src/index.ts +1 -0
- package/node_modules/@vellumai/service-contracts/src/mcp-oauth.ts +60 -0
- package/openapi.yaml +270 -10
- package/package.json +1 -1
- package/src/__tests__/agent-loop.test.ts +76 -0
- package/src/__tests__/anthropic-provider.test.ts +2 -0
- package/src/__tests__/byok-default-profile-ensure.test.ts +7 -0
- package/src/__tests__/config-get-vision-flag.test.ts +1 -1
- package/src/__tests__/config-loader-validation-reset-notice.test.ts +2 -2
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +160 -154
- package/src/__tests__/conversation-evictor.test.ts +54 -0
- package/src/__tests__/conversation-pairing.test.ts +112 -0
- package/src/__tests__/document-append-idempotency.test.ts +13 -0
- package/src/__tests__/document-create-dedupe.test.ts +13 -0
- package/src/__tests__/document-find-replace.test.ts +13 -0
- package/src/__tests__/document-revision-history.test.ts +390 -0
- package/src/__tests__/document-revision.test.ts +425 -0
- package/src/__tests__/document-tool-security.test.ts +13 -0
- package/src/__tests__/document-update-default-surface.test.ts +13 -0
- package/src/__tests__/emit-signal-routing-intent.test.ts +270 -0
- package/src/__tests__/gateway-flag-listener.test.ts +59 -6
- package/src/__tests__/headless-browser-navigate.test.ts +20 -30
- package/src/__tests__/host-cu-proxy.test.ts +79 -0
- package/src/__tests__/host-cu-routes-targeted.test.ts +14 -5
- package/src/__tests__/host-shell-tool.test.ts +6 -2
- package/src/__tests__/identity-routes.test.ts +4 -3
- package/src/__tests__/list-messages-client-message-id.test.ts +43 -0
- package/src/__tests__/llm-catalog-parity.test.ts +4 -4
- package/src/__tests__/llm-schema.test.ts +1 -1
- package/src/__tests__/notification-platform-adapter.test.ts +83 -1
- package/src/__tests__/oauth2-gateway-transport.test.ts +2 -0
- package/src/__tests__/openai-provider.test.ts +38 -11
- package/src/__tests__/pricing.test.ts +11 -0
- package/src/__tests__/public-ingress-urls.test.ts +26 -0
- package/src/__tests__/retry-thinking-tool-choice.test.ts +47 -0
- package/src/__tests__/skill-execute-input.test.ts +11 -0
- package/src/__tests__/tool-approval-handler.test.ts +4 -0
- package/src/__tests__/tool-preview-lifecycle.test.ts +26 -0
- package/src/__tests__/usage-attribution.test.ts +31 -0
- package/src/__tests__/workspace-migration-133-collapse-provider-connections.test.ts +3 -3
- package/src/__tests__/workspace-migration-160-repair-retired-fireworks-model-ids.test.ts +262 -0
- package/src/__tests__/workspace-migration-161-repair-retired-fireworks-glm-5p2-model-id.test.ts +233 -0
- package/src/acp/agent-process.ts +6 -1
- package/src/acp/client-handler.ts +6 -1
- package/src/agent/loop-unparseable-tool-retry.test.ts +333 -0
- package/src/agent/loop.ts +161 -17
- package/src/api/events/document-editor-show.ts +5 -0
- package/src/api/events/document-editor-update.ts +10 -0
- package/src/api/events/tool-result.ts +1 -0
- package/src/calls/__tests__/call-controller.test.ts +273 -1
- package/src/calls/__tests__/media-stream-server-integration.test.ts +40 -0
- package/src/calls/__tests__/media-stream-stt-session.test.ts +33 -0
- package/src/calls/__tests__/voice-session-bridge.test.ts +54 -0
- package/src/calls/call-constants.ts +10 -0
- package/src/calls/call-controller.ts +359 -25
- package/src/calls/channel-admission-reader.ts +15 -9
- package/src/calls/media-stream-server.ts +18 -0
- package/src/calls/media-stream-stt-session.ts +30 -9
- package/src/calls/twilio-provider.ts +14 -31
- package/src/calls/voice-session-bridge.ts +18 -1
- package/src/cli/commands/browser.ts +1 -2
- package/src/cli/lib/__tests__/plugin-artifact.test.ts +23 -0
- package/src/cli/lib/__tests__/plugin-catalog-visibility.test.ts +2 -0
- package/src/cli/lib/__tests__/plugin-details.test.ts +16 -0
- package/src/cli/lib/bundled-marketplace.json +54 -2
- package/src/cli/lib/inspect-plugin.ts +15 -15
- package/src/cli/lib/list-installed-plugins.ts +29 -28
- package/src/cli/lib/plugin-artifact.ts +74 -63
- package/src/cli/lib/plugin-catalog-visibility.ts +1 -0
- package/src/cli/lib/plugin-details.ts +27 -42
- package/src/cli/lib/plugin-marketplace.ts +6 -8
- package/src/cli/lib/plugin-surfaces.ts +8 -7
- package/src/cli.ts +1 -3
- package/src/config/__tests__/chat-settings.test.ts +121 -0
- package/src/config/assistant-feature-flags.ts +28 -23
- package/src/config/bundled-skills/computer-use/SKILL.md +20 -0
- package/src/config/bundled-skills/computer-use/TOOLS.json +11 -11
- package/src/config/chat-settings.ts +100 -0
- package/src/config/default-profile-catalog.ts +1 -1
- package/src/config/feature-flag-cache.ts +7 -25
- package/src/config/feature-flag-registry.json +9 -9
- package/src/config/loader.ts +5 -1
- package/src/config/profile-text-generation.ts +25 -21
- package/src/config/schemas/conversations.ts +14 -0
- package/src/config/schemas/llm.ts +1 -0
- package/src/config/schemas/notifications.ts +8 -1
- package/src/contacts/gateway-channel-read.ts +1 -1
- package/src/conversations/__tests__/auto-archive-activity.test.ts +136 -0
- package/src/conversations/__tests__/auto-archive.test.ts +304 -0
- package/src/conversations/auto-archive-activity.ts +61 -0
- package/src/conversations/auto-archive.ts +201 -0
- package/src/daemon/computer-use-observation.test.ts +81 -0
- package/src/daemon/computer-use-observation.ts +23 -0
- package/src/daemon/conversation-agent-loop-handlers.ts +18 -2
- package/src/daemon/conversation-agent-loop.ts +14 -6
- package/src/daemon/conversation-evictor.ts +50 -16
- package/src/daemon/conversation-messaging.ts +4 -0
- package/src/daemon/conversation-process.ts +15 -0
- package/src/daemon/conversation-queue-manager.ts +2 -0
- package/src/daemon/conversation.ts +2 -0
- package/src/daemon/doordash-steps.ts +1 -3
- package/src/daemon/handlers/__tests__/config-a2a-accept.test.ts +43 -1
- package/src/daemon/handlers/__tests__/config-channels.test.ts +1 -1
- package/src/daemon/handlers/config-a2a.ts +75 -2
- package/src/daemon/handlers/shared.ts +13 -2
- package/src/daemon/host-cu-proxy.ts +34 -3
- package/src/daemon/lifecycle.ts +15 -2
- package/src/daemon/process-message.ts +5 -0
- package/src/daemon/shutdown-handlers.ts +3 -0
- package/src/daemon/turn-finalization.ts +4 -0
- package/src/desktop/desktop-browser-endpoint.ts +11 -2
- package/src/desktop/desktop-chrome-policy.test.ts +1 -0
- package/src/desktop/desktop-chrome-policy.ts +7 -1
- package/src/desktop/desktop-computer-use.test.ts +14 -4
- package/src/desktop/desktop-session-manager.ts +5 -3
- package/src/documents/document-revisions-store.ts +153 -0
- package/src/documents/document-store.ts +471 -133
- package/src/followups/followup-store.ts +0 -4
- package/src/i18n/__tests__/i18n.test.ts +25 -0
- package/src/i18n/assistant-text.ts +18 -0
- package/src/i18n/index.ts +1 -0
- package/src/i18n/messages.ts +11 -0
- package/src/inbound/__tests__/oauth-callback-url.test.ts +25 -2
- package/src/inbound/oauth-callback-url.ts +30 -1
- package/src/inbound/public-ingress-urls.ts +12 -2
- package/src/ipc/gateway-client.test.ts +40 -0
- package/src/ipc/gateway-client.ts +12 -29
- package/src/ipc/gateway-flag-listener.ts +7 -10
- package/src/live-voice/__tests__/live-voice-agent-turn.test.ts +71 -0
- package/src/live-voice/__tests__/live-voice-flux-turn-end.test.ts +83 -0
- package/src/live-voice/__tests__/protocol.test.ts +77 -0
- package/src/live-voice/__tests__/share-targets.test.ts +163 -0
- package/src/live-voice/live-voice-session.ts +23 -6
- package/src/live-voice/protocol.ts +22 -0
- package/src/live-voice/share-targets.ts +225 -0
- package/src/mcp/__tests__/mcp-oauth-client-registration.test.ts +144 -3
- package/src/mcp/__tests__/mcp-oauth-provider.test.ts +2 -0
- package/src/mcp/client.ts +1 -19
- package/src/mcp/mcp-oauth-provider.ts +66 -49
- package/src/media/openrouter-image-service.ts +39 -22
- package/src/monitoring/__tests__/oom-inheritance-sweep.test.ts +86 -0
- package/src/monitoring/__tests__/oom-kill-reporter.test.ts +374 -0
- package/src/monitoring/control.ts +2 -1
- package/src/monitoring/oom-inheritance-sweep.ts +96 -0
- package/src/monitoring/oom-kill-reporter.ts +469 -0
- package/src/monitoring/resource-sampler.ts +30 -0
- package/src/notifications/README.md +53 -4
- package/src/notifications/__tests__/assistant-initiated-thread.test.ts +254 -0
- package/src/notifications/__tests__/broadcaster.test.ts +558 -6
- package/src/notifications/__tests__/completion-policy.test.ts +37 -0
- package/src/notifications/__tests__/from-me-alert-integration.test.ts +192 -0
- package/src/notifications/adapters/platform.ts +14 -0
- package/src/notifications/assistant-initiated-thread.ts +101 -0
- package/src/notifications/broadcaster.ts +94 -16
- package/src/notifications/chat-reply-policy.ts +11 -0
- package/src/notifications/completion-policy.ts +12 -2
- package/src/notifications/conversation-pairing.ts +15 -70
- package/src/notifications/emit-signal.ts +34 -3
- package/src/notifications/types.ts +2 -0
- package/src/oauth/connect-orchestrator.ts +1 -2
- package/src/persistence/__tests__/conversation-auto-archive.test.ts +330 -0
- package/src/persistence/conversation-auto-archive.ts +170 -0
- package/src/persistence/conversation-crud.ts +2 -1
- package/src/persistence/conversation-queries.ts +15 -8
- package/src/persistence/embeddings/embed.test.ts +96 -0
- package/src/persistence/embeddings/embed.ts +20 -2
- package/src/persistence/embeddings/embedding-gemini.test.ts +39 -3
- package/src/persistence/embeddings/embedding-gemini.ts +39 -1
- package/src/persistence/embeddings/embedding-local.ts +6 -0
- package/src/persistence/embeddings/qdrant-manager.ts +5 -0
- package/src/persistence/job-utils.ts +10 -0
- package/src/persistence/migrations/384-add-document-revision.test.ts +83 -0
- package/src/persistence/migrations/384-add-document-revision.ts +21 -0
- package/src/persistence/migrations/385-create-document-revisions.test.ts +67 -0
- package/src/persistence/migrations/385-create-document-revisions.ts +28 -0
- package/src/persistence/migrations/386-conversations-last-reopened-at.ts +10 -0
- package/src/persistence/migrations/__tests__/386-conversations-last-reopened-at.test.ts +32 -0
- package/src/persistence/schema/conversations.ts +1 -0
- package/src/persistence/schema/documents.ts +20 -0
- package/src/persistence/steps.ts +6 -0
- package/src/platform/managed-speech.ts +42 -46
- package/src/plugin-api/vision-support.test.ts +21 -21
- package/src/plugins/defaults/empty-response/hooks/post-model-call.ts +11 -6
- package/src/providers/__tests__/dispatch-connection-routing.test.ts +1 -1
- package/src/providers/__tests__/search-provider-catalog.test.ts +8 -0
- package/src/providers/__tests__/unparseable-tool-args.test.ts +25 -0
- package/src/providers/anthropic/client.ts +1 -1
- package/src/providers/fireworks/client.test.ts +168 -0
- package/src/providers/fireworks/client.ts +24 -0
- package/src/providers/inference/adapter-factory.ts +6 -0
- package/src/providers/ionet/client.test.ts +143 -0
- package/src/providers/ionet/client.ts +37 -0
- package/src/providers/model-catalog.ts +141 -84
- package/src/providers/model-intents.ts +4 -4
- package/src/providers/openai/chat-completions-provider.ts +33 -2
- package/src/providers/retry.ts +56 -4
- package/src/providers/search-provider-catalog.ts +10 -0
- package/src/providers/types.ts +10 -2
- package/src/providers/unparseable-tool-args.ts +29 -5
- package/src/providers/usage-tracking.ts +5 -3
- package/src/runtime/assistant-event-hub.ts +23 -0
- package/src/runtime/auth/__tests__/guard-tests.test.ts +0 -7
- package/src/runtime/auth/__tests__/route-policy.test.ts +1 -1
- package/src/runtime/channel-reply-delivery.ts +2 -0
- package/src/runtime/channel-retry-sweep.ts +1 -2
- package/src/runtime/http-types.ts +3 -0
- package/src/runtime/migrations/__tests__/vbundle-streaming-validator.test.ts +35 -10
- package/src/runtime/migrations/__tests__/vbundle-validator-v1-schema.test.ts +48 -0
- package/src/runtime/migrations/vbundle-builder.ts +19 -3
- package/src/runtime/migrations/vbundle-streaming-importer.ts +14 -19
- package/src/runtime/migrations/vbundle-streaming-validator.ts +14 -7
- package/src/runtime/migrations/vbundle-validator.ts +54 -1
- package/src/runtime/pending-interactions.ts +20 -2
- package/src/runtime/routes/__tests__/contact-routes.test.ts +5 -3
- package/src/runtime/routes/__tests__/conversation-list-routes.test.ts +36 -0
- package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +375 -2
- package/src/runtime/routes/__tests__/inference-send-routes.test.ts +1 -1
- package/src/runtime/routes/__tests__/user-route-dispatcher.test.ts +12 -0
- package/src/runtime/routes/contact-routes.ts +2 -2
- package/src/runtime/routes/conversation-list-routes.ts +10 -2
- package/src/runtime/routes/conversation-query-routes.ts +48 -10
- package/src/runtime/routes/conversation-routes.ts +7 -0
- package/src/runtime/routes/documents-routes.ts +185 -47
- package/src/runtime/routes/events-routes.ts +1 -3
- package/src/runtime/routes/host-app-control-routes.ts +1 -0
- package/src/runtime/routes/host-bash-routes.ts +1 -0
- package/src/runtime/routes/host-browser-routes.ts +15 -0
- package/src/runtime/routes/host-cu-routes.ts +4 -1
- package/src/runtime/routes/host-file-routes.ts +1 -0
- package/src/runtime/routes/host-proxy-result-binding.test.ts +83 -0
- package/src/runtime/routes/host-proxy-result-binding.ts +25 -0
- package/src/runtime/routes/host-transfer-routes.ts +1 -0
- package/src/runtime/routes/identity-routes.ts +3 -3
- package/src/runtime/routes/inbound-stages/background-dispatch.ts +4 -0
- package/src/runtime/routes/integrations/a2a.ts +4 -1
- package/src/runtime/routes/migration-routes.ts +3 -1
- package/src/runtime/routes/notification-routes.ts +1 -2
- package/src/runtime/routes/ui-snapshot-routes.test.ts +1 -0
- package/src/runtime/routes/user-route-dispatcher.ts +5 -1
- package/src/runtime/verification-templates.ts +1 -2
- package/src/sequence/analytics.ts +0 -4
- package/src/sequence/engine.ts +1 -11
- package/src/sequence/guardrails.ts +1 -6
- package/src/sequence/importer.ts +0 -8
- package/src/sequence/store.ts +0 -10
- package/src/signals/user-message.ts +0 -6
- package/src/skills/catalog-install.ts +52 -23
- package/src/tools/browser/browser-execution.ts +20 -23
- package/src/tools/browser/cdp-client/cdp-inspect/ws-transport.ts +1 -3
- package/src/tools/document/document-tool.ts +13 -11
- package/src/tools/executor.ts +1 -2
- package/src/tools/host-terminal/host-shell.ts +4 -2
- package/src/tools/network/__tests__/web-search.test.ts +105 -0
- package/src/tools/network/web-fetch.ts +2 -4
- package/src/tools/network/web-search.ts +239 -12
- package/src/tools/shared/filesystem/format-diff.ts +1 -2
- package/src/tools/shared/shell-output.test.ts +42 -0
- package/src/tools/shared/shell-output.ts +27 -4
- package/src/tools/skills/execute.ts +10 -7
- package/src/tools/terminal/shell.ts +3 -3
- package/src/tools/tool-approval-handler.ts +9 -1
- package/src/tools/ui-surface/visual-validation.ts +1 -2
- package/src/usage/attribution.ts +18 -1
- package/src/util/__tests__/oom-priority.test.ts +55 -0
- package/src/util/__tests__/worker-memory.test.ts +16 -0
- package/src/util/debounce.ts +0 -4
- package/src/util/oom-priority.ts +79 -0
- package/src/util/platform.ts +0 -2
- package/src/util/plugin-manifest.ts +5 -7
- package/src/util/pricing.ts +25 -0
- package/src/util/worker-memory.ts +9 -3
- package/src/util/worker-process.ts +8 -0
- package/src/workspace/byok-default-profile-ensure.ts +11 -0
- package/src/workspace/hatched-date.ts +11 -4
- package/src/workspace/migrations/160-repair-retired-fireworks-model-ids.ts +209 -0
- package/src/workspace/migrations/161-repair-retired-fireworks-glm-5p2-model-id.ts +197 -0
- package/src/workspace/migrations/registry.ts +4 -0
|
@@ -52,21 +52,76 @@ const SHA256_HEX_RE = /^[0-9a-f]{64}$/;
|
|
|
52
52
|
* the digest must be canonical lowercase hex so client-side comparison is a
|
|
53
53
|
* plain string equality with no normalization step.
|
|
54
54
|
*/
|
|
55
|
-
const PluginArtifactSchema = z
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
55
|
+
const PluginArtifactSchema = z
|
|
56
|
+
.object({
|
|
57
|
+
url: z
|
|
58
|
+
.string()
|
|
59
|
+
.url()
|
|
60
|
+
.refine((u) => u.startsWith("https://"), {
|
|
61
|
+
message: "artifact url must be an https:// URL",
|
|
62
|
+
}),
|
|
63
|
+
sha256: z
|
|
64
|
+
.string()
|
|
65
|
+
.regex(SHA256_HEX_RE, "artifact sha256 must be 64 lowercase hex chars"),
|
|
66
|
+
// Optional, non-critical metadata: a malformed label must never nullify an
|
|
67
|
+
// otherwise-valid `url` + `sha256`, so a wrong-typed value falls back to
|
|
68
|
+
// `undefined` rather than failing the whole descriptor.
|
|
69
|
+
label: z
|
|
70
|
+
.string()
|
|
71
|
+
.transform((label) => label.trim())
|
|
72
|
+
.optional()
|
|
73
|
+
.catch(undefined),
|
|
74
|
+
})
|
|
75
|
+
.transform(({ label, ...artifact }) =>
|
|
76
|
+
label ? { ...artifact, label } : artifact,
|
|
77
|
+
);
|
|
78
|
+
|
|
79
|
+
/** Upper bound on `vellum.icon`, in Unicode code points. */
|
|
80
|
+
const MAX_ICON_CODE_POINTS = 16;
|
|
81
|
+
|
|
82
|
+
const PluginIconSchema = z
|
|
83
|
+
.string()
|
|
84
|
+
.transform((icon) => icon.trim())
|
|
85
|
+
.refine(
|
|
86
|
+
(icon) => {
|
|
87
|
+
const codePoints = [...icon].length;
|
|
88
|
+
return codePoints >= 1 && codePoints <= MAX_ICON_CODE_POINTS;
|
|
89
|
+
},
|
|
90
|
+
{ message: "plugin icon must contain 1 to 16 Unicode code points" },
|
|
91
|
+
);
|
|
92
|
+
|
|
93
|
+
const LicenseSchema = z
|
|
94
|
+
.union([
|
|
95
|
+
z.string(),
|
|
96
|
+
z.object({ type: z.string() }).transform(({ type }) => type),
|
|
97
|
+
z.undefined(),
|
|
98
|
+
])
|
|
99
|
+
.catch(undefined);
|
|
100
|
+
|
|
101
|
+
export const PluginPackageJsonSchema = z
|
|
102
|
+
.object({
|
|
103
|
+
name: z.string().optional().catch(undefined),
|
|
104
|
+
version: z.string().optional().catch(undefined),
|
|
105
|
+
description: z.string().optional().catch(undefined),
|
|
106
|
+
homepage: z.string().optional().catch(undefined),
|
|
107
|
+
license: LicenseSchema.optional(),
|
|
108
|
+
peerDependencies: z
|
|
109
|
+
.record(z.string(), z.string())
|
|
110
|
+
.optional()
|
|
111
|
+
.catch(undefined),
|
|
112
|
+
vellum: z
|
|
113
|
+
.object({
|
|
114
|
+
artifact: PluginArtifactSchema.optional().catch(undefined),
|
|
115
|
+
icon: PluginIconSchema.optional().catch(undefined),
|
|
116
|
+
})
|
|
117
|
+
.optional()
|
|
118
|
+
.catch(undefined),
|
|
119
|
+
})
|
|
120
|
+
.transform(({ vellum, ...packageJson }) => ({
|
|
121
|
+
...packageJson,
|
|
122
|
+
artifact: vellum?.artifact,
|
|
123
|
+
icon: vellum?.icon,
|
|
124
|
+
}));
|
|
70
125
|
|
|
71
126
|
/**
|
|
72
127
|
* Read `vellum.artifact` from an already-parsed `package.json` value and
|
|
@@ -78,35 +133,10 @@ const PluginArtifactSchema = z.object({
|
|
|
78
133
|
export function parsePluginArtifact(
|
|
79
134
|
packageJson: unknown,
|
|
80
135
|
): PluginArtifact | null {
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
packageJson === null ||
|
|
84
|
-
Array.isArray(packageJson)
|
|
85
|
-
) {
|
|
86
|
-
return null;
|
|
87
|
-
}
|
|
88
|
-
const vellum = (packageJson as Record<string, unknown>).vellum;
|
|
89
|
-
if (typeof vellum !== "object" || vellum === null || Array.isArray(vellum)) {
|
|
90
|
-
return null;
|
|
91
|
-
}
|
|
92
|
-
const artifact = (vellum as Record<string, unknown>).artifact;
|
|
93
|
-
const parsed = PluginArtifactSchema.safeParse(artifact);
|
|
94
|
-
if (!parsed.success) {
|
|
95
|
-
return null;
|
|
96
|
-
}
|
|
97
|
-
// A blank or whitespace-only label is treated as absent so it never
|
|
98
|
-
// invalidates an otherwise well-formed `url` + `sha256` descriptor.
|
|
99
|
-
const label = parsed.data.label?.trim();
|
|
100
|
-
return {
|
|
101
|
-
url: parsed.data.url,
|
|
102
|
-
sha256: parsed.data.sha256,
|
|
103
|
-
...(label ? { label } : {}),
|
|
104
|
-
};
|
|
136
|
+
const parsed = PluginPackageJsonSchema.safeParse(packageJson);
|
|
137
|
+
return parsed.success ? (parsed.data.artifact ?? null) : null;
|
|
105
138
|
}
|
|
106
139
|
|
|
107
|
-
/** Upper bound on `vellum.icon`, in Unicode code points. */
|
|
108
|
-
const MAX_ICON_CODE_POINTS = 16;
|
|
109
|
-
|
|
110
140
|
/**
|
|
111
141
|
* Read `vellum.icon` from an already-parsed `package.json` value and return
|
|
112
142
|
* it only when it is a short, non-empty glyph (an emoji or similar). The
|
|
@@ -115,25 +145,6 @@ const MAX_ICON_CODE_POINTS = 16;
|
|
|
115
145
|
* type, empty, or too long — yields `undefined` rather than throwing.
|
|
116
146
|
*/
|
|
117
147
|
export function parsePluginIcon(packageJson: unknown): string | undefined {
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
packageJson === null ||
|
|
121
|
-
Array.isArray(packageJson)
|
|
122
|
-
) {
|
|
123
|
-
return undefined;
|
|
124
|
-
}
|
|
125
|
-
const vellum = (packageJson as Record<string, unknown>).vellum;
|
|
126
|
-
if (typeof vellum !== "object" || vellum === null || Array.isArray(vellum)) {
|
|
127
|
-
return undefined;
|
|
128
|
-
}
|
|
129
|
-
const icon = (vellum as Record<string, unknown>).icon;
|
|
130
|
-
if (typeof icon !== "string") {
|
|
131
|
-
return undefined;
|
|
132
|
-
}
|
|
133
|
-
const trimmed = icon.trim();
|
|
134
|
-
const codePoints = [...trimmed].length;
|
|
135
|
-
if (codePoints < 1 || codePoints > MAX_ICON_CODE_POINTS) {
|
|
136
|
-
return undefined;
|
|
137
|
-
}
|
|
138
|
-
return trimmed;
|
|
148
|
+
const parsed = PluginPackageJsonSchema.safeParse(packageJson);
|
|
149
|
+
return parsed.success ? parsed.data.icon : undefined;
|
|
139
150
|
}
|
|
@@ -32,15 +32,16 @@
|
|
|
32
32
|
import { existsSync, readdirSync, readFileSync } from "node:fs";
|
|
33
33
|
import { join } from "node:path";
|
|
34
34
|
|
|
35
|
+
import { z } from "zod";
|
|
36
|
+
|
|
35
37
|
import { getWorkspacePluginsDir } from "../../util/platform.js";
|
|
36
38
|
import { readPluginManifest } from "../../util/plugin-manifest.js";
|
|
37
39
|
import { readBundledPluginFile } from "./bundled-plugin-packages.js";
|
|
38
40
|
import type { FetchLike } from "./fetch-like.js";
|
|
39
41
|
import { sanitizePluginName } from "./install-from-github.js";
|
|
40
42
|
import {
|
|
41
|
-
parsePluginArtifact,
|
|
42
|
-
parsePluginIcon,
|
|
43
43
|
type PluginArtifact,
|
|
44
|
+
PluginPackageJsonSchema,
|
|
44
45
|
} from "./plugin-artifact.js";
|
|
45
46
|
import {
|
|
46
47
|
findCatalogEntry,
|
|
@@ -61,12 +62,16 @@ import {
|
|
|
61
62
|
const README_RE = /^readme(\.md|\.markdown)?$/i;
|
|
62
63
|
|
|
63
64
|
/** Entry shape returned by the GitHub Contents API for a directory listing. */
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
}
|
|
65
|
+
const GitHubContentEntrySchema = z.object({
|
|
66
|
+
name: z.string(),
|
|
67
|
+
path: z.string(),
|
|
68
|
+
type: z.enum(["file", "dir", "symlink", "submodule"]),
|
|
69
|
+
download_url: z.string().nullable(),
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
const GitHubContentListingSchema = z.array(GitHubContentEntrySchema);
|
|
73
|
+
|
|
74
|
+
type GitHubContentEntry = z.infer<typeof GitHubContentEntrySchema>;
|
|
70
75
|
|
|
71
76
|
/** The subset of `package.json` fields the detail view surfaces. */
|
|
72
77
|
interface PluginManifestFields {
|
|
@@ -249,11 +254,14 @@ function readLocalPlugin(pluginsDir: string, name: string): LocalPlugin {
|
|
|
249
254
|
manifest = parseManifest(readFileSync(packagePath, "utf8"));
|
|
250
255
|
} else {
|
|
251
256
|
const selected = readPluginManifest(target);
|
|
257
|
+
const parsedPackageJson = PluginPackageJsonSchema.safeParse(selected.raw);
|
|
252
258
|
manifest = {
|
|
253
259
|
version: selected.version ?? null,
|
|
254
260
|
description: selected.description ?? null,
|
|
255
261
|
homepage: selected.homepage ?? null,
|
|
256
|
-
license:
|
|
262
|
+
license: parsedPackageJson.success
|
|
263
|
+
? (parsedPackageJson.data.license ?? null)
|
|
264
|
+
: null,
|
|
257
265
|
artifact: null,
|
|
258
266
|
icon: null,
|
|
259
267
|
};
|
|
@@ -365,11 +373,8 @@ async function listDirSafe(
|
|
|
365
373
|
if (!res.ok) {
|
|
366
374
|
return null;
|
|
367
375
|
}
|
|
368
|
-
const
|
|
369
|
-
|
|
370
|
-
return null;
|
|
371
|
-
}
|
|
372
|
-
return body as readonly GitHubContentEntry[];
|
|
376
|
+
const parsed = GitHubContentListingSchema.safeParse(await res.json());
|
|
377
|
+
return parsed.success ? parsed.data : null;
|
|
373
378
|
} catch {
|
|
374
379
|
return null;
|
|
375
380
|
}
|
|
@@ -502,36 +507,16 @@ function safeParseManifest(raw: string): PluginManifestFields {
|
|
|
502
507
|
|
|
503
508
|
/** Extract the surfaced fields from a `package.json` body. Throws on bad JSON. */
|
|
504
509
|
function parseManifest(raw: string): PluginManifestFields {
|
|
505
|
-
const parsed
|
|
506
|
-
if (
|
|
510
|
+
const parsed = PluginPackageJsonSchema.safeParse(JSON.parse(raw));
|
|
511
|
+
if (!parsed.success) {
|
|
507
512
|
return emptyManifest();
|
|
508
513
|
}
|
|
509
|
-
const meta = parsed as Record<string, unknown>;
|
|
510
514
|
return {
|
|
511
|
-
version:
|
|
512
|
-
description:
|
|
513
|
-
homepage:
|
|
514
|
-
license:
|
|
515
|
-
artifact:
|
|
516
|
-
icon:
|
|
515
|
+
version: parsed.data.version ?? null,
|
|
516
|
+
description: parsed.data.description ?? null,
|
|
517
|
+
homepage: parsed.data.homepage ?? null,
|
|
518
|
+
license: parsed.data.license ?? null,
|
|
519
|
+
artifact: parsed.data.artifact ?? null,
|
|
520
|
+
icon: parsed.data.icon ?? null,
|
|
517
521
|
};
|
|
518
522
|
}
|
|
519
|
-
|
|
520
|
-
/**
|
|
521
|
-
* `package.json#license` is usually an SPDX string but the legacy object form
|
|
522
|
-
* `{ "type": "MIT" }` still appears in the wild — surface its `type`.
|
|
523
|
-
*/
|
|
524
|
-
function normalizeLicense(value: unknown): string | null {
|
|
525
|
-
if (typeof value === "string") {
|
|
526
|
-
return value;
|
|
527
|
-
}
|
|
528
|
-
if (
|
|
529
|
-
typeof value === "object" &&
|
|
530
|
-
value !== null &&
|
|
531
|
-
"type" in value &&
|
|
532
|
-
typeof (value as { type: unknown }).type === "string"
|
|
533
|
-
) {
|
|
534
|
-
return (value as { type: string }).type;
|
|
535
|
-
}
|
|
536
|
-
return null;
|
|
537
|
-
}
|
|
@@ -183,15 +183,13 @@ const fetchedMarketplaceManifestSchema = marketplaceManifestBaseSchema.extend({
|
|
|
183
183
|
plugins: z.array(z.unknown()),
|
|
184
184
|
});
|
|
185
185
|
|
|
186
|
+
const marketplaceSourceDiscriminatorSchema = z.object({
|
|
187
|
+
source: z.object({ source: z.unknown() }),
|
|
188
|
+
});
|
|
189
|
+
|
|
186
190
|
function readSourceDiscriminator(entry: unknown): unknown {
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
}
|
|
190
|
-
const source = (entry as Record<string, unknown>).source;
|
|
191
|
-
if (typeof source !== "object" || source === null) {
|
|
192
|
-
return null;
|
|
193
|
-
}
|
|
194
|
-
return (source as Record<string, unknown>).source;
|
|
191
|
+
const parsed = marketplaceSourceDiscriminatorSchema.safeParse(entry);
|
|
192
|
+
return parsed.success ? parsed.data.source.source : null;
|
|
195
193
|
}
|
|
196
194
|
|
|
197
195
|
/** A single reviewed plugin entry. */
|
|
@@ -23,6 +23,8 @@
|
|
|
23
23
|
import { existsSync, readdirSync, readFileSync, statSync } from "node:fs";
|
|
24
24
|
import { join } from "node:path";
|
|
25
25
|
|
|
26
|
+
import { z } from "zod";
|
|
27
|
+
|
|
26
28
|
/** A schedule the plugin declares under `schedules/`. */
|
|
27
29
|
export interface PluginScheduleSurface {
|
|
28
30
|
/** Schedule name: the declaration directory's name. */
|
|
@@ -124,15 +126,14 @@ function listSkillIds(skillsDir: string): string[] {
|
|
|
124
126
|
/** Matches a `---` delimited YAML frontmatter block at the start of a file. */
|
|
125
127
|
const FRONTMATTER_REGEX = /^---\r?\n([\s\S]*?)\r?\n---(?:\r?\n|$)/;
|
|
126
128
|
|
|
129
|
+
const ScheduleConfigSchema = z.object({
|
|
130
|
+
expression: z.string().refine((expression) => expression.trim() !== ""),
|
|
131
|
+
});
|
|
132
|
+
|
|
127
133
|
/** Read the raw `expression` string from a parsed schedule config, or `null`. */
|
|
128
134
|
function readConfigExpression(config: unknown): string | null {
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
}
|
|
132
|
-
const expression = (config as Record<string, unknown>).expression;
|
|
133
|
-
return typeof expression === "string" && expression.trim() !== ""
|
|
134
|
-
? expression
|
|
135
|
-
: null;
|
|
135
|
+
const parsed = ScheduleConfigSchema.safeParse(config);
|
|
136
|
+
return parsed.success ? parsed.data.expression : null;
|
|
136
137
|
}
|
|
137
138
|
|
|
138
139
|
/**
|
package/src/cli.ts
CHANGED
|
@@ -157,7 +157,7 @@ export async function startCli(): Promise<void> {
|
|
|
157
157
|
}),
|
|
158
158
|
);
|
|
159
159
|
|
|
160
|
-
|
|
160
|
+
return await new Promise<{
|
|
161
161
|
ok: boolean;
|
|
162
162
|
error?: string;
|
|
163
163
|
message?: string;
|
|
@@ -208,8 +208,6 @@ export async function startCli(): Promise<void> {
|
|
|
208
208
|
checkResult();
|
|
209
209
|
}
|
|
210
210
|
});
|
|
211
|
-
|
|
212
|
-
return result;
|
|
213
211
|
} catch {
|
|
214
212
|
return { ok: false };
|
|
215
213
|
}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { readFileSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { describe, expect, test } from "bun:test";
|
|
3
|
+
|
|
4
|
+
import { getWorkspaceConfigPath } from "../../util/platform.js";
|
|
5
|
+
import { loadRawConfig, saveRawConfig } from "../loader.js";
|
|
6
|
+
import { AssistantConfigSchema } from "../schema.js";
|
|
7
|
+
import { AutoArchiveConfigSchema } from "../schemas/conversations.js";
|
|
8
|
+
|
|
9
|
+
describe("chat settings config", () => {
|
|
10
|
+
test("missing settings preserve existing behavior", () => {
|
|
11
|
+
const config = AssistantConfigSchema.parse({});
|
|
12
|
+
expect(config.conversations.autoArchive).toEqual({
|
|
13
|
+
enabled: false,
|
|
14
|
+
afterDays: 7,
|
|
15
|
+
});
|
|
16
|
+
expect(config.notifications.newMessageEnabled).toBe(true);
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
test.each([1, 7, 14, 30])(
|
|
20
|
+
"accepts a %i-day inactivity interval",
|
|
21
|
+
(afterDays) => {
|
|
22
|
+
expect(AutoArchiveConfigSchema.parse({ afterDays })).toEqual({
|
|
23
|
+
enabled: false,
|
|
24
|
+
afterDays,
|
|
25
|
+
});
|
|
26
|
+
},
|
|
27
|
+
);
|
|
28
|
+
|
|
29
|
+
test.each([
|
|
30
|
+
{ conversations: { autoArchive: { enabled: "false" } } },
|
|
31
|
+
{ conversations: { autoArchive: { afterDays: 2 } } },
|
|
32
|
+
{ conversations: { autoArchive: { afterDays: 1.5 } } },
|
|
33
|
+
{ conversations: { autoArchive: { enabled: null } } },
|
|
34
|
+
{ conversations: { autoArchive: [] } },
|
|
35
|
+
{ conversations: null },
|
|
36
|
+
{ notifications: { newMessageEnabled: 1 } },
|
|
37
|
+
{ notifications: { newMessageEnabled: null } },
|
|
38
|
+
{ notifications: [] },
|
|
39
|
+
])(
|
|
40
|
+
"raw writes reject invalid settings before changing disk: %j",
|
|
41
|
+
(invalid) => {
|
|
42
|
+
const original = {
|
|
43
|
+
conversations: { autoArchive: { enabled: true, afterDays: 14 } },
|
|
44
|
+
notifications: { newMessageEnabled: false },
|
|
45
|
+
};
|
|
46
|
+
saveRawConfig(original);
|
|
47
|
+
expect(() => saveRawConfig(invalid)).toThrow();
|
|
48
|
+
expect(loadRawConfig()).toEqual(original);
|
|
49
|
+
},
|
|
50
|
+
);
|
|
51
|
+
|
|
52
|
+
test.each([
|
|
53
|
+
{ conversations: false },
|
|
54
|
+
{ conversations: [] },
|
|
55
|
+
{ conversations: null },
|
|
56
|
+
{ conversations: { autoArchive: "legacy" } },
|
|
57
|
+
{ conversations: { autoArchive: [] } },
|
|
58
|
+
{ conversations: { autoArchive: null } },
|
|
59
|
+
{ conversations: { autoArchive: { enabled: "true", afterDays: 2 } } },
|
|
60
|
+
{ conversations: { autoArchive: { enabled: null, afterDays: null } } },
|
|
61
|
+
{ notifications: "legacy" },
|
|
62
|
+
{ notifications: [] },
|
|
63
|
+
{ notifications: null },
|
|
64
|
+
{ notifications: { newMessageEnabled: "false" } },
|
|
65
|
+
{ notifications: { newMessageEnabled: null } },
|
|
66
|
+
])("raw unrelated writes preserve untouched legacy values: %j", (legacy) => {
|
|
67
|
+
const original = { ...legacy, maxStepsPerSession: 75 };
|
|
68
|
+
writeFileSync(getWorkspaceConfigPath(), JSON.stringify(original));
|
|
69
|
+
|
|
70
|
+
const updated = { ...loadRawConfig(), maxStepsPerSession: 100 };
|
|
71
|
+
saveRawConfig(updated);
|
|
72
|
+
|
|
73
|
+
expect(loadRawConfig()).toEqual({ ...original, maxStepsPerSession: 100 });
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
test.each([
|
|
77
|
+
[{ conversations: false }, { conversations: [] }],
|
|
78
|
+
[
|
|
79
|
+
{ conversations: [] },
|
|
80
|
+
{ conversations: { autoArchive: { enabled: "true" } } },
|
|
81
|
+
],
|
|
82
|
+
[
|
|
83
|
+
{ conversations: { autoArchive: null } },
|
|
84
|
+
{ conversations: { autoArchive: { afterDays: 2 } } },
|
|
85
|
+
],
|
|
86
|
+
[
|
|
87
|
+
{ notifications: { newMessageEnabled: "false" } },
|
|
88
|
+
{ notifications: { newMessageEnabled: "true" } },
|
|
89
|
+
],
|
|
90
|
+
[
|
|
91
|
+
{ conversations: { autoArchive: { enabled: "true", afterDays: 2 } } },
|
|
92
|
+
{ conversations: { autoArchive: { enabled: "true", afterDays: 3 } } },
|
|
93
|
+
],
|
|
94
|
+
])("raw writes reject changed invalid values: %j -> %j", (previous, next) => {
|
|
95
|
+
const configPath = getWorkspaceConfigPath();
|
|
96
|
+
writeFileSync(configPath, JSON.stringify(previous));
|
|
97
|
+
const originalBytes = readFileSync(configPath, "utf-8");
|
|
98
|
+
|
|
99
|
+
expect(() => saveRawConfig(next)).toThrow();
|
|
100
|
+
|
|
101
|
+
expect(readFileSync(configPath, "utf-8")).toBe(originalBytes);
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
test("raw writes can repair one leaf while preserving other legacy values", () => {
|
|
105
|
+
writeFileSync(
|
|
106
|
+
getWorkspaceConfigPath(),
|
|
107
|
+
JSON.stringify({
|
|
108
|
+
conversations: { autoArchive: { enabled: "true", afterDays: 2 } },
|
|
109
|
+
notifications: null,
|
|
110
|
+
}),
|
|
111
|
+
);
|
|
112
|
+
const repaired = {
|
|
113
|
+
conversations: { autoArchive: { enabled: true, afterDays: 2 } },
|
|
114
|
+
notifications: null,
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
saveRawConfig(repaired);
|
|
118
|
+
|
|
119
|
+
expect(loadRawConfig()).toEqual(repaired);
|
|
120
|
+
});
|
|
121
|
+
});
|
|
@@ -16,6 +16,8 @@
|
|
|
16
16
|
import { existsSync, readFileSync } from "node:fs";
|
|
17
17
|
import { dirname, join } from "node:path";
|
|
18
18
|
|
|
19
|
+
import { z } from "zod";
|
|
20
|
+
|
|
19
21
|
import { ipcGetFeatureFlags } from "../ipc/gateway-client.js";
|
|
20
22
|
import { getLogger } from "../util/logger.js";
|
|
21
23
|
import {
|
|
@@ -47,6 +49,22 @@ export type FeatureFlagDefaultsRegistry = Record<string, FeatureFlagDefault>;
|
|
|
47
49
|
let cachedDefaults: FeatureFlagDefaultsRegistry | undefined;
|
|
48
50
|
|
|
49
51
|
const REGISTRY_FILENAME = "feature-flag-registry.json";
|
|
52
|
+
const FeatureFlagRegistryEntrySchema = z
|
|
53
|
+
.object({
|
|
54
|
+
scope: z.enum(["assistant", "both"]).optional().catch(undefined),
|
|
55
|
+
key: z.string().optional().catch(undefined),
|
|
56
|
+
defaultEnabled: z
|
|
57
|
+
.union([z.boolean(), z.string()])
|
|
58
|
+
.optional()
|
|
59
|
+
.catch(undefined),
|
|
60
|
+
description: z.string().catch(""),
|
|
61
|
+
label: z.string().catch(""),
|
|
62
|
+
})
|
|
63
|
+
.optional()
|
|
64
|
+
.catch(undefined);
|
|
65
|
+
const FeatureFlagRegistrySchema = z.object({
|
|
66
|
+
flags: z.array(FeatureFlagRegistryEntrySchema),
|
|
67
|
+
});
|
|
50
68
|
|
|
51
69
|
function loadDefaultsRegistry(): FeatureFlagDefaultsRegistry {
|
|
52
70
|
if (cachedDefaults) {
|
|
@@ -100,39 +118,26 @@ function loadDefaultsRegistry(): FeatureFlagDefaultsRegistry {
|
|
|
100
118
|
* filtering to flags the backend consumes (`assistant`- and `both`-scope).
|
|
101
119
|
*/
|
|
102
120
|
function parseRegistryToDefaults(parsed: unknown): FeatureFlagDefaultsRegistry {
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
const registry = parsed as { version?: number; flags?: unknown[] };
|
|
108
|
-
if (!Array.isArray(registry.flags)) {
|
|
121
|
+
const registry = FeatureFlagRegistrySchema.safeParse(parsed);
|
|
122
|
+
if (!registry.success) {
|
|
109
123
|
return {};
|
|
110
124
|
}
|
|
111
125
|
|
|
112
126
|
const result: FeatureFlagDefaultsRegistry = {};
|
|
113
|
-
for (const
|
|
114
|
-
if (!flag || typeof flag !== "object" || Array.isArray(flag)) {
|
|
115
|
-
continue;
|
|
116
|
-
}
|
|
117
|
-
const entry = flag as Record<string, unknown>;
|
|
118
|
-
if (entry.scope !== "assistant" && entry.scope !== "both") {
|
|
119
|
-
continue;
|
|
120
|
-
}
|
|
121
|
-
if (typeof entry.key !== "string") {
|
|
122
|
-
continue;
|
|
123
|
-
}
|
|
127
|
+
for (const entry of registry.data.flags) {
|
|
124
128
|
if (
|
|
125
|
-
|
|
126
|
-
|
|
129
|
+
!entry ||
|
|
130
|
+
entry.scope === undefined ||
|
|
131
|
+
entry.key === undefined ||
|
|
132
|
+
entry.defaultEnabled === undefined
|
|
127
133
|
) {
|
|
128
134
|
continue;
|
|
129
135
|
}
|
|
130
136
|
|
|
131
|
-
result[entry.key
|
|
137
|
+
result[entry.key] = {
|
|
132
138
|
defaultEnabled: entry.defaultEnabled,
|
|
133
|
-
description:
|
|
134
|
-
|
|
135
|
-
label: typeof entry.label === "string" ? entry.label : "",
|
|
139
|
+
description: entry.description,
|
|
140
|
+
label: entry.label,
|
|
136
141
|
};
|
|
137
142
|
}
|
|
138
143
|
return result;
|
|
@@ -37,6 +37,26 @@ The tree is walked to a limited depth to keep steps fast, and says when it was
|
|
|
37
37
|
cut off. If the element you need is not in it, call `computer_use_observe` with
|
|
38
38
|
`full_tree: true`.
|
|
39
39
|
|
|
40
|
+
## Coordinates and live editing
|
|
41
|
+
|
|
42
|
+
Prefer element IDs from the latest observation. Click, scroll and drag
|
|
43
|
+
coordinates are **screen points**, not screenshot pixels. For a desktop
|
|
44
|
+
screenshot, use the per-axis conversion in its observation metadata. If you
|
|
45
|
+
crop or resize the image, map back to the original screenshot first. Observe
|
|
46
|
+
again after the layout changes; an older image is not a current target.
|
|
47
|
+
|
|
48
|
+
During live editing, use the app's zoom and track-height controls, then inspect
|
|
49
|
+
the fresh screenshot. Avoid installing image libraries or repeatedly cropping
|
|
50
|
+
and measuring screenshot pixels just to locate a UI control or waveform.
|
|
51
|
+
Reserve scripted image or media analysis for tasks that actually require it.
|
|
52
|
+
If an action does not produce the expected visible result, check focus and the
|
|
53
|
+
coordinate mapping before repeating it.
|
|
54
|
+
|
|
55
|
+
Waveform pixels do not reveal spoken words. If an edit depends on audio you
|
|
56
|
+
cannot hear or a boundary you cannot see, ask for a timestamp, a user-positioned
|
|
57
|
+
playhead, or the source media. Do not guess the cut point or claim an edit
|
|
58
|
+
succeeded without observing its result.
|
|
59
|
+
|
|
40
60
|
## Scripting apps (macOS)
|
|
41
61
|
|
|
42
62
|
Reach for `computer_use_run_applescript` first when an app can be driven by
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
},
|
|
22
22
|
"full_tree": {
|
|
23
23
|
"type": "boolean",
|
|
24
|
-
"description": "On connected personal computers, walk the accessibility tree to full depth. The virtual desktop uses a bounded tree; use screenshot
|
|
24
|
+
"description": "On connected personal computers, walk the accessibility tree to full depth. The virtual desktop uses a bounded tree; use coordinates converted from the screenshot metadata when an element is not listed."
|
|
25
25
|
},
|
|
26
26
|
"target_client_id": {
|
|
27
27
|
"type": "string",
|
|
@@ -57,11 +57,11 @@
|
|
|
57
57
|
},
|
|
58
58
|
"x": {
|
|
59
59
|
"type": "integer",
|
|
60
|
-
"description": "X coordinate
|
|
60
|
+
"description": "X in screen points, not screenshot pixels. Apply the latest observation's coordinate mapping (fallback when no element_id)."
|
|
61
61
|
},
|
|
62
62
|
"y": {
|
|
63
63
|
"type": "integer",
|
|
64
|
-
"description": "Y coordinate
|
|
64
|
+
"description": "Y in screen points, not screenshot pixels. Apply the latest observation's coordinate mapping (fallback when no element_id)."
|
|
65
65
|
},
|
|
66
66
|
"reasoning": {
|
|
67
67
|
"type": "string",
|
|
@@ -170,11 +170,11 @@
|
|
|
170
170
|
},
|
|
171
171
|
"x": {
|
|
172
172
|
"type": "integer",
|
|
173
|
-
"description": "X coordinate
|
|
173
|
+
"description": "X in screen points, not screenshot pixels. Apply the latest observation's coordinate mapping (fallback when no element_id)."
|
|
174
174
|
},
|
|
175
175
|
"y": {
|
|
176
176
|
"type": "integer",
|
|
177
|
-
"description": "Y coordinate
|
|
177
|
+
"description": "Y in screen points, not screenshot pixels. Apply the latest observation's coordinate mapping (fallback when no element_id)."
|
|
178
178
|
},
|
|
179
179
|
"direction": {
|
|
180
180
|
"type": "string",
|
|
@@ -222,11 +222,11 @@
|
|
|
222
222
|
},
|
|
223
223
|
"x": {
|
|
224
224
|
"type": "integer",
|
|
225
|
-
"description": "Source X coordinate (fallback when no element_id)"
|
|
225
|
+
"description": "Source X in screen points, not screenshot pixels. Apply the latest observation's coordinate mapping (fallback when no element_id)."
|
|
226
226
|
},
|
|
227
227
|
"y": {
|
|
228
228
|
"type": "integer",
|
|
229
|
-
"description": "Source Y coordinate (fallback when no element_id)"
|
|
229
|
+
"description": "Source Y in screen points, not screenshot pixels. Apply the latest observation's coordinate mapping (fallback when no element_id)."
|
|
230
230
|
},
|
|
231
231
|
"to_element_id": {
|
|
232
232
|
"type": "integer",
|
|
@@ -234,11 +234,11 @@
|
|
|
234
234
|
},
|
|
235
235
|
"to_x": {
|
|
236
236
|
"type": "integer",
|
|
237
|
-
"description": "Destination X coordinate (fallback when no to_element_id)"
|
|
237
|
+
"description": "Destination X in screen points, not screenshot pixels. Apply the latest observation's coordinate mapping (fallback when no to_element_id)."
|
|
238
238
|
},
|
|
239
239
|
"to_y": {
|
|
240
240
|
"type": "integer",
|
|
241
|
-
"description": "Destination Y coordinate (fallback when no to_element_id)"
|
|
241
|
+
"description": "Destination Y in screen points, not screenshot pixels. Apply the latest observation's coordinate mapping (fallback when no to_element_id)."
|
|
242
242
|
},
|
|
243
243
|
"reasoning": {
|
|
244
244
|
"type": "string",
|
|
@@ -408,11 +408,11 @@
|
|
|
408
408
|
},
|
|
409
409
|
"x": {
|
|
410
410
|
"type": "integer",
|
|
411
|
-
"description": "click, double_click, right_click, scroll: X coordinate
|
|
411
|
+
"description": "click, double_click, right_click, scroll: X in screen points, not screenshot pixels. Apply the latest observation's coordinate mapping (fallback when no element_id)."
|
|
412
412
|
},
|
|
413
413
|
"y": {
|
|
414
414
|
"type": "integer",
|
|
415
|
-
"description": "click, double_click, right_click, scroll: Y coordinate
|
|
415
|
+
"description": "click, double_click, right_click, scroll: Y in screen points, not screenshot pixels. Apply the latest observation's coordinate mapping (fallback when no element_id)."
|
|
416
416
|
},
|
|
417
417
|
"direction": {
|
|
418
418
|
"type": "string",
|