Files
foundry-core/internal/platform/canvas_capabilities.go
T
codex-bot 56b6181598
Foundry Go checks / check (push) Failing after 2m49s
Foundry Go checks / check (pull_request) Failing after 2m41s
Resolve canonical AI targets at their current canvas owner
2026-11-08 01:16:28 +08:00

59 lines
2.8 KiB
Go

package platform
import (
"context"
"net/http"
"time"
)
func (s *service) canvasCapabilities(w http.ResponseWriter, r *http.Request, project, account string) error {
if r.Method != http.MethodGet {
reject(w, 405, "method_not_allowed")
return nil
}
tx, err := s.db.BeginTx(r.Context(), nil)
if err != nil {
return err
}
defer tx.Rollback()
kind, _, err := lockMaterial(r.Context(), tx, project, r.PathValue("workspace"), r.PathValue("material"), account)
if err != nil {
return err
}
if kind != "board" {
reject(w, 400, "invalid_material")
return nil
}
if err = tx.Rollback(); err != nil {
return err
}
ctx, cancel := context.WithTimeout(r.Context(), 5*time.Second)
defer cancel()
models, modelErr := s.canvasAI.Models(ctx)
textAvailable, imageAvailable := false, false
if modelErr == nil {
for _, model := range models {
if model.Available {
textAvailable = textAvailable || canvasModelSupports(model, "text")
imageAvailable = imageAvailable || canvasModelSupports(model, "image")
}
}
}
ai := response{"canonical_target_identity": true, "owner_relocation": true, "admission_protocol_version": 2, "admission_receipt_query": true, "mutation_protocol_version": 2, "terminal_rejection_receipts": true, "available": textAvailable || imageAvailable, "text": textAvailable, "image": imageAvailable}
if modelErr != nil {
ai["unavailable_reason"] = "codex_unavailable"
}
reply(w, 200, response{
"contract_version": "canvas-headless-2026-11-07",
"canonical_model": response{"document_format_version": canvasModelFormat, "protocol_version": canvasModelProtocol, "explicit_migration": true, "stable_element_ids": true, "atomic_multi_canvas": true, "logical_operation_dedup": true, "legacy_snapshot_writes": false},
"supported_schema_versions": []int{1, 2},
"edge_features": response{"appearance": true, "markers": true, "bezier_chain": true, "animation_configuration": true, "execution_events": true},
"subgraphs": response{"create": true, "migrate": true, "undo": true, "members": true, "dissolve": true, "connections": true, "member_positions": true},
"ai": ai,
"assets": response{"client_resource_ids": true, "receipt_protocol_version": 2, "previews": true, "preview_max_edge": 1024, "originals_preserved": true},
"limits": response{"max_depth": 8, "max_nodes": 500, "max_edges": 2000, "max_frame_ports": 4000, "max_curve_knots": 64, "max_snapshot_bytes": canvasLimit, "max_active_tasks": 128, "max_run_steps": 64, "max_execution_depth": 32, "max_input_images": 4, "max_input_image_bytes": 20 << 20, "max_coordinate": 1000000},
"retention": response{"idempotency_min_seconds": 86400, "undo_receipt_seconds": 604800},
})
return nil
}