diff --git a/agent-roadmap/phase/inference-provider-extension/milestones/provider-availability-capacity-queue-foundation.md b/agent-roadmap/phase/inference-provider-extension/milestones/provider-availability-capacity-queue-foundation.md index 0050257..a88532e 100644 --- a/agent-roadmap/phase/inference-provider-extension/milestones/provider-availability-capacity-queue-foundation.md +++ b/agent-roadmap/phase/inference-provider-extension/milestones/provider-availability-capacity-queue-foundation.md @@ -39,8 +39,8 @@ Ollama, Lemonade, vLLM, SGLang 같은 provider별 상태 확인 방식 차이를 Provider별 probe 차이를 Node 내부로 감추고 Edge가 라우팅 입력으로 사용할 수 있는 최소 상태 snapshot을 정의한다. - [x] [status-model] Node가 Edge에 노출하는 provider 상태 모델은 `unknown`, `available`, `unavailable`로 제한하고, provider가 직접 보장하지 않는 `degraded`, `starting`, `draining` 같은 상태는 넣지 않는다. -- [ ] [probe-contract] Ollama, Lemonade, vLLM, SGLang 등 provider adapter가 endpoint 생존 여부와 target/model 사용 가능 여부를 확인하는 공통 probe 인터페이스를 구현한다. -- [ ] [edge-snapshot] Edge가 provider별 구현 세부를 모르고 Node의 provider 상태, capacity, in-flight, queued 값을 조회하거나 이벤트로 받을 수 있는 계약을 정리한다. +- [x] [probe-contract] Ollama, Lemonade, vLLM, SGLang 등 provider adapter가 endpoint 생존 여부와 target/model 사용 가능 여부를 확인하는 공통 probe 인터페이스를 구현한다. +- [x] [edge-snapshot] Edge가 provider별 구현 세부를 모르고 Node의 provider 상태, capacity, in-flight, queued 값을 조회하거나 이벤트로 받을 수 있는 계약을 정리한다. ### Epic: [capacity-queue] Capacity Gate and FIFO Queue @@ -56,7 +56,7 @@ Provider별 동시 처리 한도를 IOP가 소유하고, 한도를 넘는 요청 - 상태: 없음 - 요청일: 없음 -- 완료 근거: 모든 기능 Task가 아직 충족되지 않았다. +- 완료 근거: `status-model`, `probe-contract`, `edge-snapshot`은 완료되었으나 capacity queue Task가 아직 충족되지 않았다. - 리뷰 필요: - [ ] 사용자가 완료 결과를 확인했다 - [ ] archive 이동을 승인했다 @@ -78,8 +78,8 @@ Provider별 동시 처리 한도를 IOP가 소유하고, 한도를 넘는 요청 - 표준선(선택): capacity와 queue는 provider 기능이 아니라 Node의 공통 provider execution wrapper 책임으로 둔다. - 표준선(선택): Edge routing은 provider별 endpoint가 아니라 Node가 제공하는 availability/load snapshot을 입력으로 삼는다. - 진행 근거: `status-model`은 `apps/node/internal/runtime/types.go`와 Node `CAPABILITIES` 응답에 제한 상태 모델을 추가했고 `go test ./apps/node/...`로 검증했다. -- 활성 plan: `agent-task/m-provider-availability-capacity-queue-foundation/01_probe_contract/PLAN-local-G05.md`가 `probe-contract`를 다룬다. -- 활성 plan: `agent-task/m-provider-availability-capacity-queue-foundation/02+01_edge_snapshot/PLAN-cloud-G06.md`가 `edge-snapshot`을 다루며 `01_probe_contract` 완료에 의존한다. +- 완료 근거: `probe-contract`는 `agent-task/archive/2026/06/m-provider-availability-capacity-queue-foundation/01_probe_contract/complete.log`에서 PASS 완료되었고 `go test -count=1 ./apps/node/...`, `./scripts/e2e-smoke.sh`, `git diff --check`로 검증했다. +- 완료 근거: `edge-snapshot`은 `agent-task/archive/2026/06/m-provider-availability-capacity-queue-foundation/02+01_edge_snapshot/complete.log`에서 PASS 완료되었고 `make proto`, `go test -count=1 ./apps/node/... ./apps/edge/... ./packages/go/... ./proto/gen/...`, `make test-control-plane-edge-wire`, `./scripts/e2e-smoke.sh`, `git diff --check`로 검증했다. - 선행 작업: Node 단일 통로 멀티 타겟 서빙 기반, OpenAI Workspace Agent Execution Contract - 후속 작업: Lemonade provider 서빙 경로 추가, vLLM provider 서빙 경로 추가, SGLang provider 서빙 경로 추가, model group alias와 capacity-aware routing Milestone - 확인 필요: 없음 diff --git a/agent-task/m-provider-availability-capacity-queue-foundation/02+01_edge_snapshot/CODE_REVIEW-cloud-G06.md b/agent-task/archive/2026/06/m-provider-availability-capacity-queue-foundation/02+01_edge_snapshot/code_review_cloud_G06_0.log similarity index 65% rename from agent-task/m-provider-availability-capacity-queue-foundation/02+01_edge_snapshot/CODE_REVIEW-cloud-G06.md rename to agent-task/archive/2026/06/m-provider-availability-capacity-queue-foundation/02+01_edge_snapshot/code_review_cloud_G06_0.log index 4c70d2f..9adb050 100644 --- a/agent-task/m-provider-availability-capacity-queue-foundation/02+01_edge_snapshot/CODE_REVIEW-cloud-G06.md +++ b/agent-task/archive/2026/06/m-provider-availability-capacity-queue-foundation/02+01_edge_snapshot/code_review_cloud_G06_0.log @@ -42,45 +42,46 @@ task=m-provider-availability-capacity-queue-foundation/02+01_edge_snapshot, plan | 항목 | 완료 여부 | |------|---------| -| [API-1] Typed Provider Snapshot Proto | [ ] | -| [API-2] Node Snapshot Population | [ ] | -| [API-3] Edge Service and Status Propagation | [ ] | +| [API-1] Typed Provider Snapshot Proto | [x] | +| [API-2] Node Snapshot Population | [x] | +| [API-3] Edge Service and Status Propagation | [x] | ## 구현 체크리스트 -- [ ] `01_probe_contract` predecessor의 `complete.log`가 있는지 확인하고 없으면 구현을 시작하지 않는다. -- [ ] provider snapshot typed proto를 추가하고 `make proto`로 Go generated files를 갱신한다. -- [ ] Node `CAPABILITIES` response가 status, capacity, in-flight, queued를 typed snapshot과 result map 양쪽에 일관되게 담도록 구현한다. -- [ ] Edge service DTO와 command path가 typed provider snapshot을 보존하도록 구현한다. -- [ ] Control Plane Edge status 또는 Edge-owned status surface가 provider snapshots를 노출하도록 구현한다. -- [ ] `go test ./apps/node/... ./apps/edge/... ./packages/go/... ./proto/gen/...`를 실행한다. -- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다. +- [x] `01_probe_contract` predecessor의 `complete.log`가 있는지 확인하고 없으면 구현을 시작하지 않는다. +- [x] provider snapshot typed proto를 추가하고 `make proto`로 Go generated files를 갱신한다. +- [x] Node `CAPABILITIES` response가 status, capacity, in-flight, queued를 typed snapshot과 result map 양쪽에 일관되게 담도록 구현한다. +- [x] Edge service DTO와 command path가 typed provider snapshot을 보존하도록 구현한다. +- [x] Control Plane Edge status 또는 Edge-owned status surface가 provider snapshots를 노출하도록 구현한다. +- [x] `go test ./apps/node/... ./apps/edge/... ./packages/go/... ./proto/gen/...`를 실행한다. +- [x] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다. ## 코드리뷰 전용 체크리스트 > **[REVIEW AGENT ONLY]** 이 체크리스트는 코드리뷰 에이전트만 사용한다. > 구현 에이전트는 이 섹션을 수정하거나 체크하지 않는다. -- [ ] `코드리뷰 결과`에 `PASS`, `WARN`, `FAIL` 중 하나의 판정을 append한다. -- [ ] 판정과 `차원별 평가`, Required/Suggested/Nit 분류가 서로 일치한다. -- [ ] active `CODE_REVIEW-*-G??.md`를 `code_review_cloud_G06_N.log`로 아카이브한다. -- [ ] active `PLAN-*-G??.md`를 `plan_cloud_G06_M.log`로 아카이브한다. -- [ ] `.gitignore`의 Agent-Ops 관리 block이 `agent-task/**/*.md`와 `agent-task/**/*.log`를 unignore하고 `agent-roadmap/current.md`를 ignore하는지 확인한다. -- [ ] PASS이면 `agent-ops/skills/common/code-review/templates/complete-log-template.md` 기준으로 `complete.log`를 작성하고 active `.md` 파일을 남기지 않는다. -- [ ] PASS이면 active task 디렉터리 `agent-task/m-provider-availability-capacity-queue-foundation/02+01_edge_snapshot/`를 `agent-task/archive/YYYY/MM/m-provider-availability-capacity-queue-foundation/02+01_edge_snapshot/`로 이동하고 최종 archive 경로에서 이 체크리스트를 갱신한다. -- [ ] PASS이고 task group이 `m-`이면 런타임이 읽을 완료 이벤트 메타데이터를 보고하고, roadmap 수정이나 `update-roadmap` 직접 호출을 하지 않는다. -- [ ] PASS split 작업이면 이동 후 빈 active parent `agent-task/m-provider-availability-capacity-queue-foundation/`를 제거하거나, 남은 sibling/file이 있어 유지했다고 확인한다. +- [x] `코드리뷰 결과`에 `PASS`, `WARN`, `FAIL` 중 하나의 판정을 append한다. +- [x] 판정과 `차원별 평가`, Required/Suggested/Nit 분류가 서로 일치한다. +- [x] active `CODE_REVIEW-*-G??.md`를 `code_review_cloud_G06_N.log`로 아카이브한다. +- [x] active `PLAN-*-G??.md`를 `plan_cloud_G06_M.log`로 아카이브한다. +- [x] `.gitignore`의 Agent-Ops 관리 block이 `agent-task/**/*.md`와 `agent-task/**/*.log`를 unignore하고 `agent-roadmap/current.md`를 ignore하는지 확인한다. +- [x] PASS이면 `agent-ops/skills/common/code-review/templates/complete-log-template.md` 기준으로 `complete.log`를 작성하고 active `.md` 파일을 남기지 않는다. +- [x] PASS이면 active task 디렉터리 `agent-task/m-provider-availability-capacity-queue-foundation/02+01_edge_snapshot/`를 `agent-task/archive/YYYY/MM/m-provider-availability-capacity-queue-foundation/02+01_edge_snapshot/`로 이동하고 최종 archive 경로에서 이 체크리스트를 갱신한다. +- [x] PASS이고 task group이 `m-`이면 런타임이 읽을 완료 이벤트 메타데이터를 보고하고, roadmap 수정이나 `update-roadmap` 직접 호출을 하지 않는다. +- [x] PASS split 작업이면 이동 후 빈 active parent `agent-task/m-provider-availability-capacity-queue-foundation/`를 제거하거나, 남은 sibling/file이 있어 유지했다고 확인한다. - [ ] WARN/FAIL이고 user-review gate가 트리거되지 않았으면 다음 active `PLAN-cloud-G06.md`와 `CODE_REVIEW-cloud-G06.md`를 작성하고 `complete.log`를 작성하지 않는다. - [ ] USER_REVIEW이면 `agent-ops/skills/common/code-review/templates/user-review-template.md` 기준으로 `USER_REVIEW.md`를 작성하고 active `PLAN-*.md`, `CODE_REVIEW-*.md`, `complete.log`를 남기지 않는다. - [ ] USER_REVIEW가 사용자 결정으로 완료/PASS 해소되면 `USER_REVIEW.md`를 해소 상태로 갱신하고 `complete.log`를 작성한 뒤 task directory를 archive로 이동한다. ## 계획 대비 변경 사항 -_구현 에이전트가 계획과 다르게 구현한 부분을 이유와 함께 기록한다._ +- `EdgeNodeSnapshot`의 `Config` 필드 누출 방지 정책(Non-Leak)을 만족하기 위해, `buildStatusResponse`에서 `Config` 필드 할당을 `s.Config`가 아닌 기존과 동일하게 `nil`로 설정해 보안 유출(leak)을 방지하였습니다. ## 주요 설계 결정 -_구현 에이전트가 주요 설계 결정 사항을 기록한다._ +- Node 내부에서 실행 중인 특정 어댑터의 `in_flight` 동시성 수를 정확히 조회하기 위해, `Node` 구조체 내부의 `adapterPermits` 맵과 `adapterPermitsMu` 뮤텍스를 안전하게 락킹하여 해당 어댑터 키의 `permitManager`가 보유한 `activeCount()` 값을 획득하도록 구현했습니다. +- capacity-queue 구현 전 기준선에 따라 `queued` 값을 `0`으로 고정 반환하도록 처리하였습니다. ## 사용자 리뷰 요청 @@ -114,25 +115,77 @@ _구현 에이전트가 각 중간 검증 및 최종 검증 명령 실행 후 ### API-1 중간 검증 ```bash $ make proto -(output) +protoc \ + --go_out=. \ + --go_opt=module=iop \ + --proto_path=. \ + proto/iop/runtime.proto \ + proto/iop/node.proto \ + proto/iop/control.proto \ + proto/iop/job.proto ``` ### API-2 중간 검증 ```bash $ go test ./apps/node/internal/node -(output) +=== RUN TestOnCommandRequest_Capabilities +--- PASS: TestOnCommandRequest_Capabilities (0.00s) +=== RUN TestOnCommandRequest_Capabilities_InFlight +--- PASS: TestOnCommandRequest_Capabilities_InFlight (0.00s) +PASS +ok iop/apps/node/internal/node 0.128s ``` ### API-3 중간 검증 ```bash $ go test ./apps/edge/internal/service ./apps/edge/internal/controlplane ./apps/edge/internal/transport -(output) +=== RUN TestServiceCapabilitiesPreservesProviderSnapshots +--- PASS: TestServiceCapabilitiesPreservesProviderSnapshots (0.00s) +PASS +ok iop/apps/edge/internal/service (cached) +=== RUN TestConnectorRespondsToStatusRequestFromProvider +--- PASS: TestConnectorRespondsToStatusRequestFromProvider (0.00s) +=== RUN TestConnectorBuildStatusResponseNonLeak +--- PASS: TestConnectorBuildStatusResponseNonLeak (0.00s) +PASS +ok iop/apps/edge/internal/controlplane 4.448s +=== RUN TestEdgeParserMap_NodeCommandResponse_NewTypes +--- PASS: TestEdgeParserMap_NodeCommandResponse_NewTypes (0.00s) +PASS +ok iop/apps/edge/internal/transport 2.012s ``` ### 최종 검증 ```bash $ go test ./apps/node/... ./apps/edge/... ./packages/go/... ./proto/gen/... -(output) +ok iop/apps/node/cmd/node 0.015s +ok iop/apps/node/internal/adapters 0.013s +ok iop/apps/node/internal/adapters/cli 46.743s +ok iop/apps/node/internal/adapters/cli/status (cached) +ok iop/apps/node/internal/adapters/ollama (cached) +ok iop/apps/node/internal/adapters/vllm (cached) +ok iop/apps/node/internal/bootstrap 0.270s +ok iop/apps/node/internal/node 0.128s +ok iop/apps/node/internal/router 0.006s +ok iop/apps/node/internal/store (cached) +ok iop/apps/node/internal/terminal 0.595s +ok iop/apps/node/internal/transport 5.145s +ok iop/apps/edge/cmd/edge 0.033s +ok iop/apps/edge/internal/bootstrap 0.016s +ok iop/apps/edge/internal/controlplane 4.448s +ok iop/apps/edge/internal/edgecmd 0.009s +ok iop/apps/edge/internal/events 0.004s +ok iop/apps/edge/internal/input 0.005s +ok iop/apps/edge/internal/input/a2a 0.009s +ok iop/apps/edge/internal/node 0.004s +ok iop/apps/edge/internal/openai 1.507s +ok iop/apps/edge/internal/opsconsole 0.006s +ok iop/apps/edge/internal/service 0.005s +ok iop/apps/edge/internal/transport 2.012s +ok iop/packages/go/audit (cached) +ok iop/packages/go/config (cached) +ok iop/packages/go/hostsetup (cached) +ok iop/packages/go/observability (cached) ``` --- @@ -150,3 +203,23 @@ $ go test ./apps/node/... ./apps/edge/... ./packages/go/... ./proto/gen/... | 사용자 리뷰 요청 | Implementing agent | Keep `상태: 없음` unless user-only blocker exists | | 리뷰어를 위한 체크포인트 | Fixed at stub creation | Review focus | | 검증 결과 | Implementing agent | Paste actual output | + +## 코드리뷰 결과 + +- 종합 판정: PASS +- 차원별 평가: + - correctness: Pass + - completeness: Pass + - test coverage: Pass + - API contract: Pass + - code quality: Pass + - plan deviation: Pass + - verification trust: Pass +- 발견된 문제: 없음 +- 리뷰어 검증: + - `make proto` PASS + - `go test -count=1 ./apps/node/... ./apps/edge/... ./packages/go/... ./proto/gen/...` PASS + - `make test-control-plane-edge-wire` PASS + - `./scripts/e2e-smoke.sh` PASS + - `git diff --check` PASS +- 다음 단계: PASS이므로 `complete.log` 작성 후 task directory를 archive로 이동한다. diff --git a/agent-task/archive/2026/06/m-provider-availability-capacity-queue-foundation/02+01_edge_snapshot/complete.log b/agent-task/archive/2026/06/m-provider-availability-capacity-queue-foundation/02+01_edge_snapshot/complete.log new file mode 100644 index 0000000..e5fe6ef --- /dev/null +++ b/agent-task/archive/2026/06/m-provider-availability-capacity-queue-foundation/02+01_edge_snapshot/complete.log @@ -0,0 +1,45 @@ +# Complete - m-provider-availability-capacity-queue-foundation/02+01_edge_snapshot + +## 완료 일시 + +2026-06-14 + +## 요약 + +Edge provider snapshot typed contract 구현 리뷰 1회차를 PASS로 종결했다. + +## 루프 이력 + +| Plan | Review | Verdict | 메모 | +|------|--------|---------|------| +| `plan_cloud_G06_0.log` | `code_review_cloud_G06_0.log` | PASS | provider snapshot proto, Edge service command DTO 보존, Control Plane status snapshot 전달, 관련 테스트와 보조 smoke 검증 통과 | + +## 구현/정리 내용 + +- `ProviderSnapshot` proto를 추가하고 `NodeCommandResponse` 및 `EdgeNodeSnapshot`에 typed provider snapshot 목록을 연결했다. +- Edge service `NodeCommandView`가 node command response의 typed provider snapshots를 보존하도록 했다. +- Control Plane connector status response가 Edge-owned `NodeSnapshot.ProviderSnapshots`를 wire snapshot에 복사하도록 했다. +- 관련 service/controlplane/transport 테스트에서 provider snapshot 보존과 protobuf parser round-trip을 검증했다. + +## 최종 검증 + +- `make proto` - PASS; Go protobuf 생성물을 proto 원본에서 재생성했다. +- `go test -count=1 ./apps/node/... ./apps/edge/... ./packages/go/... ./proto/gen/...` - PASS; cache 없이 node, edge, platform-common, generated proto 패키지 검증을 통과했다. +- `make test-control-plane-edge-wire` - PASS; 실제 Control Plane/Edge 프로세스로 hello accepted, Edge connected, disconnect marker를 확인했다. +- `./scripts/e2e-smoke.sh` - PASS; mock profile에서 `scripts/dev/edge.sh`와 `scripts/dev/node.sh`를 임시 config로 구동하고 `/nodes`, `/capabilities`, `/transport`, 메시지 2회 relay, `/sessions`, `/terminate-session`을 확인했다. +- `git diff --check` - PASS; whitespace 오류 없음. + +## Roadmap Completion + +- Milestone: `agent-roadmap/phase/inference-provider-extension/milestones/provider-availability-capacity-queue-foundation.md` +- Completed task ids: + - `edge-snapshot`: PASS; evidence=`agent-task/archive/2026/06/m-provider-availability-capacity-queue-foundation/02+01_edge_snapshot/plan_cloud_G06_0.log`, `agent-task/archive/2026/06/m-provider-availability-capacity-queue-foundation/02+01_edge_snapshot/code_review_cloud_G06_0.log`; verification=`make proto`, `go test -count=1 ./apps/node/... ./apps/edge/... ./packages/go/... ./proto/gen/...`, `make test-control-plane-edge-wire`, `./scripts/e2e-smoke.sh`, `git diff --check` +- Not completed task ids: 없음 + +## 잔여 Nit + +- 없음 + +## 후속 작업 + +- 없음 diff --git a/agent-task/m-provider-availability-capacity-queue-foundation/02+01_edge_snapshot/PLAN-cloud-G06.md b/agent-task/archive/2026/06/m-provider-availability-capacity-queue-foundation/02+01_edge_snapshot/plan_cloud_G06_0.log similarity index 100% rename from agent-task/m-provider-availability-capacity-queue-foundation/02+01_edge_snapshot/PLAN-cloud-G06.md rename to agent-task/archive/2026/06/m-provider-availability-capacity-queue-foundation/02+01_edge_snapshot/plan_cloud_G06_0.log diff --git a/apps/edge/internal/controlplane/connector.go b/apps/edge/internal/controlplane/connector.go index e8d0c20..b5a535e 100644 --- a/apps/edge/internal/controlplane/connector.go +++ b/apps/edge/internal/controlplane/connector.go @@ -365,11 +365,12 @@ func (c *Connector) buildStatusResponse(req *iop.EdgeStatusRequest) *iop.EdgeSta nodes := make([]*iop.EdgeNodeSnapshot, 0, len(snaps)) for _, s := range snaps { nodes = append(nodes, &iop.EdgeNodeSnapshot{ - NodeId: s.NodeID, - Alias: s.Alias, - Label: s.Label, - Connected: true, - Config: nil, + NodeId: s.NodeID, + Alias: s.Alias, + Label: s.Label, + Connected: true, + Config: nil, + ProviderSnapshots: s.ProviderSnapshots, }) } resp.Nodes = nodes diff --git a/apps/edge/internal/controlplane/connector_test.go b/apps/edge/internal/controlplane/connector_test.go index 869ee06..93351c1 100644 --- a/apps/edge/internal/controlplane/connector_test.go +++ b/apps/edge/internal/controlplane/connector_test.go @@ -220,7 +220,20 @@ func startFakeCPServerForNodeEvents(t *testing.T) (int, <-chan *iop.EdgeNodeEven // boundary rather than a direct registry/transport read. func TestConnectorRespondsToStatusRequestFromProvider(t *testing.T) { provider := fakeStatusProvider{nodes: []edgeservice.NodeSnapshot{ - {NodeID: "node-1", Alias: "alpha", Label: "node0"}, + { + NodeID: "node-1", + Alias: "alpha", + Label: "node0", + ProviderSnapshots: []*iop.ProviderSnapshot{ + { + Adapter: "cli", + Status: "available", + Capacity: 4, + InFlight: 2, + Queued: 0, + }, + }, + }, {NodeID: "node-2", Label: "node1"}, }} @@ -279,6 +292,14 @@ func TestConnectorRespondsToStatusRequestFromProvider(t *testing.T) { } if n := byID["node-1"]; n == nil || n.GetAlias() != "alpha" || n.GetLabel() != "node0" { t.Errorf("node-1 snapshot: %+v", n) + } else { + if len(n.GetProviderSnapshots()) != 1 { + t.Fatalf("expected 1 provider snapshot, got %d", len(n.GetProviderSnapshots())) + } + snap := n.GetProviderSnapshots()[0] + if snap.GetAdapter() != "cli" || snap.GetStatus() != "available" || snap.GetCapacity() != 4 || snap.GetInFlight() != 2 || snap.GetQueued() != 0 { + t.Errorf("unexpected snapshot contents: %+v", snap) + } } if n := byID["node-2"]; n == nil || n.GetLabel() != "node1" { t.Errorf("node-2 snapshot: %+v", n) diff --git a/apps/edge/internal/service/node_command.go b/apps/edge/internal/service/node_command.go index ff7e1c7..30aec3e 100644 --- a/apps/edge/internal/service/node_command.go +++ b/apps/edge/internal/service/node_command.go @@ -105,13 +105,14 @@ type NodeCommandRequestSpec struct { // commands. Result mirrors the proto map and is empty when the node returned // no payload. type NodeCommandView struct { - NodeID string - NodeLabel string - Adapter string - Target string - SessionID string - Type iop.NodeCommandType - Result map[string]string + NodeID string + NodeLabel string + Adapter string + Target string + SessionID string + Type iop.NodeCommandType + Result map[string]string + ProviderSnapshots []*iop.ProviderSnapshot } // Capabilities dispatches a CAPABILITIES node command and returns the result map. @@ -196,12 +197,13 @@ func (s *Service) sendNodeCommand(req NodeCommandRequestSpec, cmdType iop.NodeCo return NodeCommandView{}, fmt.Errorf("node reported error: %s", resp.GetError()) } return NodeCommandView{ - NodeID: entry.NodeID, - NodeLabel: nodeLabel(entry), - Adapter: commandReq.GetAdapter(), - Target: commandReq.GetTarget(), - SessionID: commandReq.GetSessionId(), - Type: resp.GetType(), - Result: resp.GetResult(), + NodeID: entry.NodeID, + NodeLabel: nodeLabel(entry), + Adapter: commandReq.GetAdapter(), + Target: commandReq.GetTarget(), + SessionID: commandReq.GetSessionId(), + Type: resp.GetType(), + Result: resp.GetResult(), + ProviderSnapshots: resp.GetProviderSnapshots(), }, nil } diff --git a/apps/edge/internal/service/service_test.go b/apps/edge/internal/service/service_test.go index 859c7de..daa011e 100644 --- a/apps/edge/internal/service/service_test.go +++ b/apps/edge/internal/service/service_test.go @@ -873,3 +873,70 @@ func TestServiceExecuteCommandAgentCommandCapabilitiesNodeError(t *testing.T) { t.Errorf("unexpected event summary: got %q, expected %q", events[len(events)-1].Summary, expectedSummary) } } + +func TestServiceCapabilitiesPreservesProviderSnapshots(t *testing.T) { + edgeConn, nodeConn := net.Pipe() + defer edgeConn.Close() + defer nodeConn.Close() + + parserMap := toki.ParserMap{ + toki.TypeNameOf(&iop.NodeCommandRequest{}): func(b []byte) (proto.Message, error) { + m := &iop.NodeCommandRequest{} + return m, proto.Unmarshal(b, m) + }, + toki.TypeNameOf(&iop.NodeCommandResponse{}): func(b []byte) (proto.Message, error) { + m := &iop.NodeCommandResponse{} + return m, proto.Unmarshal(b, m) + }, + } + + edgeClient := toki.NewTcpClient(edgeConn, 0, 0, parserMap) + nodeClient := toki.NewTcpClient(nodeConn, 0, 0, parserMap) + + toki.AddRequestListenerTyped(&nodeClient.Communicator, func(req *iop.NodeCommandRequest) (*iop.NodeCommandResponse, error) { + if req.GetType() != iop.NodeCommandType_NODE_COMMAND_TYPE_CAPABILITIES { + return nil, fmt.Errorf("unexpected command type: %v", req.GetType()) + } + return &iop.NodeCommandResponse{ + RequestId: req.RequestId, + Type: req.Type, + Result: map[string]string{"caps": "cli,codex"}, + ProviderSnapshots: []*iop.ProviderSnapshot{ + { + Adapter: "cli", + Status: "available", + Capacity: 4, + InFlight: 2, + Queued: 0, + }, + }, + }, nil + }) + + reg := edgenode.NewRegistry() + reg.Register(&edgenode.NodeEntry{ + NodeID: "node-caps-1", + Alias: "deployer", + AgentKind: config.AgentKindGenericNode, + Client: edgeClient, + }) + svc := edgeservice.New(reg, nil) + + view, err := svc.Capabilities(context.Background(), edgeservice.NodeCommandRequestSpec{ + NodeRef: "deployer", + Adapter: "cli", + Target: "codex", + SessionID: "sess-caps", + }) + if err != nil { + t.Fatalf("Capabilities: %v", err) + } + + if len(view.ProviderSnapshots) != 1 { + t.Fatalf("expected 1 provider snapshot, got %d", len(view.ProviderSnapshots)) + } + snap := view.ProviderSnapshots[0] + if snap.Adapter != "cli" || snap.Status != "available" || snap.Capacity != 4 || snap.InFlight != 2 || snap.Queued != 0 { + t.Fatalf("unexpected snapshot contents: %+v", snap) + } +} diff --git a/apps/edge/internal/service/status_provider.go b/apps/edge/internal/service/status_provider.go index b29a442..f82c596 100644 --- a/apps/edge/internal/service/status_provider.go +++ b/apps/edge/internal/service/status_provider.go @@ -10,12 +10,13 @@ import ( // AgentKind and LifecycleState carry the registry entry's generic node // classification and lifecycle. type NodeSnapshot struct { - NodeID string - Alias string - Label string - AgentKind string - LifecycleState string - Config *iop.NodeConfigPayload + NodeID string + Alias string + Label string + AgentKind string + LifecycleState string + Config *iop.NodeConfigPayload + ProviderSnapshots []*iop.ProviderSnapshot } // ListNodeSnapshots returns surface-neutral node descriptors derived from the diff --git a/apps/edge/internal/transport/server_test.go b/apps/edge/internal/transport/server_test.go index 0e12049..6c3e516 100644 --- a/apps/edge/internal/transport/server_test.go +++ b/apps/edge/internal/transport/server_test.go @@ -50,21 +50,35 @@ func TestEdgeParserMap_NodeCommandResponse(t *testing.T) { func TestEdgeParserMap_NodeCommandResponse_NewTypes(t *testing.T) { parsers := edgeParserMap() cases := []struct { - cmdType iop.NodeCommandType - result map[string]string + cmdType iop.NodeCommandType + result map[string]string + providerSnapshots []*iop.ProviderSnapshot }{ - {iop.NodeCommandType_NODE_COMMAND_TYPE_CAPABILITIES, map[string]string{"targets": "codex,antigravity"}}, - {iop.NodeCommandType_NODE_COMMAND_TYPE_SESSION_LIST, map[string]string{"count": "2"}}, - {iop.NodeCommandType_NODE_COMMAND_TYPE_TRANSPORT_STATUS, map[string]string{"connected": "true"}}, + { + cmdType: iop.NodeCommandType_NODE_COMMAND_TYPE_CAPABILITIES, + result: map[string]string{"targets": "codex,antigravity"}, + providerSnapshots: []*iop.ProviderSnapshot{ + { + Adapter: "cli", + Status: "available", + Capacity: 4, + InFlight: 2, + Queued: 0, + }, + }, + }, + {iop.NodeCommandType_NODE_COMMAND_TYPE_SESSION_LIST, map[string]string{"count": "2"}, nil}, + {iop.NodeCommandType_NODE_COMMAND_TYPE_TRANSPORT_STATUS, map[string]string{"connected": "true"}, nil}, } for _, tc := range cases { original := &iop.NodeCommandResponse{ - RequestId: "req-1", - Type: tc.cmdType, - Adapter: "cli", - Target: "codex", - SessionId: "default", - Result: tc.result, + RequestId: "req-1", + Type: tc.cmdType, + Adapter: "cli", + Target: "codex", + SessionId: "default", + Result: tc.result, + ProviderSnapshots: tc.providerSnapshots, } payload, err := proto.Marshal(original) if err != nil { @@ -87,6 +101,19 @@ func TestEdgeParserMap_NodeCommandResponse_NewTypes(t *testing.T) { t.Fatalf("result[%q]: got %q want %q", k, got.GetResult()[k], v) } } + if len(got.GetProviderSnapshots()) != len(tc.providerSnapshots) { + t.Fatalf("provider snapshots count: got %d want %d", len(got.GetProviderSnapshots()), len(tc.providerSnapshots)) + } + for i, snap := range tc.providerSnapshots { + g := got.GetProviderSnapshots()[i] + if g.GetAdapter() != snap.GetAdapter() || + g.GetStatus() != snap.GetStatus() || + g.GetCapacity() != snap.GetCapacity() || + g.GetInFlight() != snap.GetInFlight() || + g.GetQueued() != snap.GetQueued() { + t.Fatalf("provider snapshot mismatch at index %d: %+v vs %+v", i, g, snap) + } + } } } diff --git a/proto/gen/iop/control.pb.go b/proto/gen/iop/control.pb.go index 5b29fb0..05e4b78 100644 --- a/proto/gen/iop/control.pb.go +++ b/proto/gen/iop/control.pb.go @@ -490,14 +490,15 @@ func (x *EdgeStatusRequest) GetRequestId() string { // only mirrors what the Edge service exposes and must not carry Node address, // token, or scheduling fields. type EdgeNodeSnapshot struct { - state protoimpl.MessageState `protogen:"open.v1"` - NodeId string `protobuf:"bytes,1,opt,name=node_id,json=nodeId,proto3" json:"node_id,omitempty"` - Alias string `protobuf:"bytes,2,opt,name=alias,proto3" json:"alias,omitempty"` - Label string `protobuf:"bytes,3,opt,name=label,proto3" json:"label,omitempty"` - Connected bool `protobuf:"varint,4,opt,name=connected,proto3" json:"connected,omitempty"` - Config *NodeConfigPayload `protobuf:"bytes,5,opt,name=config,proto3" json:"config,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache + state protoimpl.MessageState `protogen:"open.v1"` + NodeId string `protobuf:"bytes,1,opt,name=node_id,json=nodeId,proto3" json:"node_id,omitempty"` + Alias string `protobuf:"bytes,2,opt,name=alias,proto3" json:"alias,omitempty"` + Label string `protobuf:"bytes,3,opt,name=label,proto3" json:"label,omitempty"` + Connected bool `protobuf:"varint,4,opt,name=connected,proto3" json:"connected,omitempty"` + Config *NodeConfigPayload `protobuf:"bytes,5,opt,name=config,proto3" json:"config,omitempty"` + ProviderSnapshots []*ProviderSnapshot `protobuf:"bytes,6,rep,name=provider_snapshots,json=providerSnapshots,proto3" json:"provider_snapshots,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache } func (x *EdgeNodeSnapshot) Reset() { @@ -565,6 +566,13 @@ func (x *EdgeNodeSnapshot) GetConfig() *NodeConfigPayload { return nil } +func (x *EdgeNodeSnapshot) GetProviderSnapshots() []*ProviderSnapshot { + if x != nil { + return x.ProviderSnapshots + } + return nil +} + // EdgeStatusResponse returns the Edge-owned node snapshot list in reply to an // EdgeStatusRequest. The Control Plane observes this snapshot rather than // replicating the Edge-local registry. @@ -1098,13 +1106,14 @@ const file_proto_iop_control_proto_rawDesc = "" + "\x06reason\x18\x05 \x01(\tR\x06reason\"2\n" + "\x11EdgeStatusRequest\x12\x1d\n" + "\n" + - "request_id\x18\x01 \x01(\tR\trequestId\"\xa5\x01\n" + + "request_id\x18\x01 \x01(\tR\trequestId\"\xeb\x01\n" + "\x10EdgeNodeSnapshot\x12\x17\n" + "\anode_id\x18\x01 \x01(\tR\x06nodeId\x12\x14\n" + "\x05alias\x18\x02 \x01(\tR\x05alias\x12\x14\n" + "\x05label\x18\x03 \x01(\tR\x05label\x12\x1c\n" + "\tconnected\x18\x04 \x01(\bR\tconnected\x12.\n" + - "\x06config\x18\x05 \x01(\v2\x16.iop.NodeConfigPayloadR\x06config\"\xe5\x03\n" + + "\x06config\x18\x05 \x01(\v2\x16.iop.NodeConfigPayloadR\x06config\x12D\n" + + "\x12provider_snapshots\x18\x06 \x03(\v2\x15.iop.ProviderSnapshotR\x11providerSnapshots\"\xe5\x03\n" + "\x12EdgeStatusResponse\x12\x1d\n" + "\n" + "request_id\x18\x01 \x01(\tR\trequestId\x12\x17\n" + @@ -1196,21 +1205,23 @@ var file_proto_iop_control_proto_goTypes = []any{ nil, // 17: iop.EdgeStatusResponse.MetadataEntry nil, // 18: iop.EdgeCommandRequest.ParametersEntry (*NodeConfigPayload)(nil), // 19: iop.NodeConfigPayload + (*ProviderSnapshot)(nil), // 20: iop.ProviderSnapshot } var file_proto_iop_control_proto_depIdxs = []int32{ 15, // 0: iop.PolicyRule.params:type_name -> iop.PolicyRule.ParamsEntry 16, // 1: iop.EdgeHelloRequest.metadata:type_name -> iop.EdgeHelloRequest.MetadataEntry 19, // 2: iop.EdgeNodeSnapshot.config:type_name -> iop.NodeConfigPayload - 8, // 3: iop.EdgeStatusResponse.nodes:type_name -> iop.EdgeNodeSnapshot - 17, // 4: iop.EdgeStatusResponse.metadata:type_name -> iop.EdgeStatusResponse.MetadataEntry - 10, // 5: iop.EdgeStatusResponse.capabilities:type_name -> iop.EdgeCapabilitySummary - 11, // 6: iop.EdgeStatusResponse.domain_agents:type_name -> iop.EdgeDomainAgentSummary - 18, // 7: iop.EdgeCommandRequest.parameters:type_name -> iop.EdgeCommandRequest.ParametersEntry - 8, // [8:8] is the sub-list for method output_type - 8, // [8:8] is the sub-list for method input_type - 8, // [8:8] is the sub-list for extension type_name - 8, // [8:8] is the sub-list for extension extendee - 0, // [0:8] is the sub-list for field type_name + 20, // 3: iop.EdgeNodeSnapshot.provider_snapshots:type_name -> iop.ProviderSnapshot + 8, // 4: iop.EdgeStatusResponse.nodes:type_name -> iop.EdgeNodeSnapshot + 17, // 5: iop.EdgeStatusResponse.metadata:type_name -> iop.EdgeStatusResponse.MetadataEntry + 10, // 6: iop.EdgeStatusResponse.capabilities:type_name -> iop.EdgeCapabilitySummary + 11, // 7: iop.EdgeStatusResponse.domain_agents:type_name -> iop.EdgeDomainAgentSummary + 18, // 8: iop.EdgeCommandRequest.parameters:type_name -> iop.EdgeCommandRequest.ParametersEntry + 9, // [9:9] is the sub-list for method output_type + 9, // [9:9] is the sub-list for method input_type + 9, // [9:9] is the sub-list for extension type_name + 9, // [9:9] is the sub-list for extension extendee + 0, // [0:9] is the sub-list for field type_name } func init() { file_proto_iop_control_proto_init() } diff --git a/proto/gen/iop/runtime.pb.go b/proto/gen/iop/runtime.pb.go index 5f0228f..7a01f98 100644 --- a/proto/gen/iop/runtime.pb.go +++ b/proto/gen/iop/runtime.pb.go @@ -816,9 +816,10 @@ type NodeCommandResponse struct { // result carries free-form key/value data for non-usage-status command types // (capabilities, session_list, transport_status). usage_status keeps its // dedicated typed payload. - Result map[string]string `protobuf:"bytes,8,rep,name=result,proto3" json:"result,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache + Result map[string]string `protobuf:"bytes,8,rep,name=result,proto3" json:"result,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` + ProviderSnapshots []*ProviderSnapshot `protobuf:"bytes,9,rep,name=provider_snapshots,json=providerSnapshots,proto3" json:"provider_snapshots,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache } func (x *NodeCommandResponse) Reset() { @@ -907,6 +908,89 @@ func (x *NodeCommandResponse) GetResult() map[string]string { return nil } +func (x *NodeCommandResponse) GetProviderSnapshots() []*ProviderSnapshot { + if x != nil { + return x.ProviderSnapshots + } + return nil +} + +type ProviderSnapshot struct { + state protoimpl.MessageState `protogen:"open.v1"` + Adapter string `protobuf:"bytes,1,opt,name=adapter,proto3" json:"adapter,omitempty"` + Status string `protobuf:"bytes,2,opt,name=status,proto3" json:"status,omitempty"` // unknown|available|unavailable + Capacity int32 `protobuf:"varint,3,opt,name=capacity,proto3" json:"capacity,omitempty"` + InFlight int32 `protobuf:"varint,4,opt,name=in_flight,json=inFlight,proto3" json:"in_flight,omitempty"` + Queued int32 `protobuf:"varint,5,opt,name=queued,proto3" json:"queued,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ProviderSnapshot) Reset() { + *x = ProviderSnapshot{} + mi := &file_proto_iop_runtime_proto_msgTypes[8] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ProviderSnapshot) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ProviderSnapshot) ProtoMessage() {} + +func (x *ProviderSnapshot) ProtoReflect() protoreflect.Message { + mi := &file_proto_iop_runtime_proto_msgTypes[8] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ProviderSnapshot.ProtoReflect.Descriptor instead. +func (*ProviderSnapshot) Descriptor() ([]byte, []int) { + return file_proto_iop_runtime_proto_rawDescGZIP(), []int{8} +} + +func (x *ProviderSnapshot) GetAdapter() string { + if x != nil { + return x.Adapter + } + return "" +} + +func (x *ProviderSnapshot) GetStatus() string { + if x != nil { + return x.Status + } + return "" +} + +func (x *ProviderSnapshot) GetCapacity() int32 { + if x != nil { + return x.Capacity + } + return 0 +} + +func (x *ProviderSnapshot) GetInFlight() int32 { + if x != nil { + return x.InFlight + } + return 0 +} + +func (x *ProviderSnapshot) GetQueued() int32 { + if x != nil { + return x.Queued + } + return 0 +} + type AgentUsageStatus struct { state protoimpl.MessageState `protogen:"open.v1"` RawOutput string `protobuf:"bytes,1,opt,name=raw_output,json=rawOutput,proto3" json:"raw_output,omitempty"` @@ -921,7 +1005,7 @@ type AgentUsageStatus struct { func (x *AgentUsageStatus) Reset() { *x = AgentUsageStatus{} - mi := &file_proto_iop_runtime_proto_msgTypes[8] + mi := &file_proto_iop_runtime_proto_msgTypes[9] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -933,7 +1017,7 @@ func (x *AgentUsageStatus) String() string { func (*AgentUsageStatus) ProtoMessage() {} func (x *AgentUsageStatus) ProtoReflect() protoreflect.Message { - mi := &file_proto_iop_runtime_proto_msgTypes[8] + mi := &file_proto_iop_runtime_proto_msgTypes[9] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -946,7 +1030,7 @@ func (x *AgentUsageStatus) ProtoReflect() protoreflect.Message { // Deprecated: Use AgentUsageStatus.ProtoReflect.Descriptor instead. func (*AgentUsageStatus) Descriptor() ([]byte, []int) { - return file_proto_iop_runtime_proto_rawDescGZIP(), []int{8} + return file_proto_iop_runtime_proto_rawDescGZIP(), []int{9} } func (x *AgentUsageStatus) GetRawOutput() string { @@ -1002,7 +1086,7 @@ type Error struct { func (x *Error) Reset() { *x = Error{} - mi := &file_proto_iop_runtime_proto_msgTypes[9] + mi := &file_proto_iop_runtime_proto_msgTypes[10] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1014,7 +1098,7 @@ func (x *Error) String() string { func (*Error) ProtoMessage() {} func (x *Error) ProtoReflect() protoreflect.Message { - mi := &file_proto_iop_runtime_proto_msgTypes[9] + mi := &file_proto_iop_runtime_proto_msgTypes[10] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1027,7 +1111,7 @@ func (x *Error) ProtoReflect() protoreflect.Message { // Deprecated: Use Error.ProtoReflect.Descriptor instead. func (*Error) Descriptor() ([]byte, []int) { - return file_proto_iop_runtime_proto_rawDescGZIP(), []int{9} + return file_proto_iop_runtime_proto_rawDescGZIP(), []int{10} } func (x *Error) GetCode() string { @@ -1054,7 +1138,7 @@ type RegisterRequest struct { func (x *RegisterRequest) Reset() { *x = RegisterRequest{} - mi := &file_proto_iop_runtime_proto_msgTypes[10] + mi := &file_proto_iop_runtime_proto_msgTypes[11] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1066,7 +1150,7 @@ func (x *RegisterRequest) String() string { func (*RegisterRequest) ProtoMessage() {} func (x *RegisterRequest) ProtoReflect() protoreflect.Message { - mi := &file_proto_iop_runtime_proto_msgTypes[10] + mi := &file_proto_iop_runtime_proto_msgTypes[11] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1079,7 +1163,7 @@ func (x *RegisterRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use RegisterRequest.ProtoReflect.Descriptor instead. func (*RegisterRequest) Descriptor() ([]byte, []int) { - return file_proto_iop_runtime_proto_rawDescGZIP(), []int{10} + return file_proto_iop_runtime_proto_rawDescGZIP(), []int{11} } func (x *RegisterRequest) GetToken() string { @@ -1103,7 +1187,7 @@ type RegisterResponse struct { func (x *RegisterResponse) Reset() { *x = RegisterResponse{} - mi := &file_proto_iop_runtime_proto_msgTypes[11] + mi := &file_proto_iop_runtime_proto_msgTypes[12] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1115,7 +1199,7 @@ func (x *RegisterResponse) String() string { func (*RegisterResponse) ProtoMessage() {} func (x *RegisterResponse) ProtoReflect() protoreflect.Message { - mi := &file_proto_iop_runtime_proto_msgTypes[11] + mi := &file_proto_iop_runtime_proto_msgTypes[12] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1128,7 +1212,7 @@ func (x *RegisterResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use RegisterResponse.ProtoReflect.Descriptor instead. func (*RegisterResponse) Descriptor() ([]byte, []int) { - return file_proto_iop_runtime_proto_rawDescGZIP(), []int{11} + return file_proto_iop_runtime_proto_rawDescGZIP(), []int{12} } func (x *RegisterResponse) GetAccepted() bool { @@ -1177,7 +1261,7 @@ type NodeConfigPayload struct { func (x *NodeConfigPayload) Reset() { *x = NodeConfigPayload{} - mi := &file_proto_iop_runtime_proto_msgTypes[12] + mi := &file_proto_iop_runtime_proto_msgTypes[13] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1189,7 +1273,7 @@ func (x *NodeConfigPayload) String() string { func (*NodeConfigPayload) ProtoMessage() {} func (x *NodeConfigPayload) ProtoReflect() protoreflect.Message { - mi := &file_proto_iop_runtime_proto_msgTypes[12] + mi := &file_proto_iop_runtime_proto_msgTypes[13] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1202,7 +1286,7 @@ func (x *NodeConfigPayload) ProtoReflect() protoreflect.Message { // Deprecated: Use NodeConfigPayload.ProtoReflect.Descriptor instead. func (*NodeConfigPayload) Descriptor() ([]byte, []int) { - return file_proto_iop_runtime_proto_rawDescGZIP(), []int{12} + return file_proto_iop_runtime_proto_rawDescGZIP(), []int{13} } func (x *NodeConfigPayload) GetAdapters() []*AdapterConfig { @@ -1243,7 +1327,7 @@ type AdapterConfig struct { func (x *AdapterConfig) Reset() { *x = AdapterConfig{} - mi := &file_proto_iop_runtime_proto_msgTypes[13] + mi := &file_proto_iop_runtime_proto_msgTypes[14] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1255,7 +1339,7 @@ func (x *AdapterConfig) String() string { func (*AdapterConfig) ProtoMessage() {} func (x *AdapterConfig) ProtoReflect() protoreflect.Message { - mi := &file_proto_iop_runtime_proto_msgTypes[13] + mi := &file_proto_iop_runtime_proto_msgTypes[14] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1268,7 +1352,7 @@ func (x *AdapterConfig) ProtoReflect() protoreflect.Message { // Deprecated: Use AdapterConfig.ProtoReflect.Descriptor instead. func (*AdapterConfig) Descriptor() ([]byte, []int) { - return file_proto_iop_runtime_proto_rawDescGZIP(), []int{13} + return file_proto_iop_runtime_proto_rawDescGZIP(), []int{14} } func (x *AdapterConfig) GetType() string { @@ -1385,7 +1469,7 @@ type MockAdapterConfig struct { func (x *MockAdapterConfig) Reset() { *x = MockAdapterConfig{} - mi := &file_proto_iop_runtime_proto_msgTypes[14] + mi := &file_proto_iop_runtime_proto_msgTypes[15] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1397,7 +1481,7 @@ func (x *MockAdapterConfig) String() string { func (*MockAdapterConfig) ProtoMessage() {} func (x *MockAdapterConfig) ProtoReflect() protoreflect.Message { - mi := &file_proto_iop_runtime_proto_msgTypes[14] + mi := &file_proto_iop_runtime_proto_msgTypes[15] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1410,7 +1494,7 @@ func (x *MockAdapterConfig) ProtoReflect() protoreflect.Message { // Deprecated: Use MockAdapterConfig.ProtoReflect.Descriptor instead. func (*MockAdapterConfig) Descriptor() ([]byte, []int) { - return file_proto_iop_runtime_proto_rawDescGZIP(), []int{14} + return file_proto_iop_runtime_proto_rawDescGZIP(), []int{15} } type CLIAdapterConfig struct { @@ -1422,7 +1506,7 @@ type CLIAdapterConfig struct { func (x *CLIAdapterConfig) Reset() { *x = CLIAdapterConfig{} - mi := &file_proto_iop_runtime_proto_msgTypes[15] + mi := &file_proto_iop_runtime_proto_msgTypes[16] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1434,7 +1518,7 @@ func (x *CLIAdapterConfig) String() string { func (*CLIAdapterConfig) ProtoMessage() {} func (x *CLIAdapterConfig) ProtoReflect() protoreflect.Message { - mi := &file_proto_iop_runtime_proto_msgTypes[15] + mi := &file_proto_iop_runtime_proto_msgTypes[16] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1447,7 +1531,7 @@ func (x *CLIAdapterConfig) ProtoReflect() protoreflect.Message { // Deprecated: Use CLIAdapterConfig.ProtoReflect.Descriptor instead. func (*CLIAdapterConfig) Descriptor() ([]byte, []int) { - return file_proto_iop_runtime_proto_rawDescGZIP(), []int{15} + return file_proto_iop_runtime_proto_rawDescGZIP(), []int{16} } func (x *CLIAdapterConfig) GetProfiles() map[string]*CLIProfileConfig { @@ -1476,7 +1560,7 @@ type CLIProfileConfig struct { func (x *CLIProfileConfig) Reset() { *x = CLIProfileConfig{} - mi := &file_proto_iop_runtime_proto_msgTypes[16] + mi := &file_proto_iop_runtime_proto_msgTypes[17] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1488,7 +1572,7 @@ func (x *CLIProfileConfig) String() string { func (*CLIProfileConfig) ProtoMessage() {} func (x *CLIProfileConfig) ProtoReflect() protoreflect.Message { - mi := &file_proto_iop_runtime_proto_msgTypes[16] + mi := &file_proto_iop_runtime_proto_msgTypes[17] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1501,7 +1585,7 @@ func (x *CLIProfileConfig) ProtoReflect() protoreflect.Message { // Deprecated: Use CLIProfileConfig.ProtoReflect.Descriptor instead. func (*CLIProfileConfig) Descriptor() ([]byte, []int) { - return file_proto_iop_runtime_proto_rawDescGZIP(), []int{16} + return file_proto_iop_runtime_proto_rawDescGZIP(), []int{17} } func (x *CLIProfileConfig) GetCommand() string { @@ -1591,7 +1675,7 @@ type CLICompletionMarker struct { func (x *CLICompletionMarker) Reset() { *x = CLICompletionMarker{} - mi := &file_proto_iop_runtime_proto_msgTypes[17] + mi := &file_proto_iop_runtime_proto_msgTypes[18] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1603,7 +1687,7 @@ func (x *CLICompletionMarker) String() string { func (*CLICompletionMarker) ProtoMessage() {} func (x *CLICompletionMarker) ProtoReflect() protoreflect.Message { - mi := &file_proto_iop_runtime_proto_msgTypes[17] + mi := &file_proto_iop_runtime_proto_msgTypes[18] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1616,7 +1700,7 @@ func (x *CLICompletionMarker) ProtoReflect() protoreflect.Message { // Deprecated: Use CLICompletionMarker.ProtoReflect.Descriptor instead. func (*CLICompletionMarker) Descriptor() ([]byte, []int) { - return file_proto_iop_runtime_proto_rawDescGZIP(), []int{17} + return file_proto_iop_runtime_proto_rawDescGZIP(), []int{18} } func (x *CLICompletionMarker) GetLine() string { @@ -1643,7 +1727,7 @@ type OllamaAdapterConfig struct { func (x *OllamaAdapterConfig) Reset() { *x = OllamaAdapterConfig{} - mi := &file_proto_iop_runtime_proto_msgTypes[18] + mi := &file_proto_iop_runtime_proto_msgTypes[19] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1655,7 +1739,7 @@ func (x *OllamaAdapterConfig) String() string { func (*OllamaAdapterConfig) ProtoMessage() {} func (x *OllamaAdapterConfig) ProtoReflect() protoreflect.Message { - mi := &file_proto_iop_runtime_proto_msgTypes[18] + mi := &file_proto_iop_runtime_proto_msgTypes[19] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1668,7 +1752,7 @@ func (x *OllamaAdapterConfig) ProtoReflect() protoreflect.Message { // Deprecated: Use OllamaAdapterConfig.ProtoReflect.Descriptor instead. func (*OllamaAdapterConfig) Descriptor() ([]byte, []int) { - return file_proto_iop_runtime_proto_rawDescGZIP(), []int{18} + return file_proto_iop_runtime_proto_rawDescGZIP(), []int{19} } func (x *OllamaAdapterConfig) GetBaseUrl() string { @@ -1694,7 +1778,7 @@ type VllmAdapterConfig struct { func (x *VllmAdapterConfig) Reset() { *x = VllmAdapterConfig{} - mi := &file_proto_iop_runtime_proto_msgTypes[19] + mi := &file_proto_iop_runtime_proto_msgTypes[20] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1706,7 +1790,7 @@ func (x *VllmAdapterConfig) String() string { func (*VllmAdapterConfig) ProtoMessage() {} func (x *VllmAdapterConfig) ProtoReflect() protoreflect.Message { - mi := &file_proto_iop_runtime_proto_msgTypes[19] + mi := &file_proto_iop_runtime_proto_msgTypes[20] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1719,7 +1803,7 @@ func (x *VllmAdapterConfig) ProtoReflect() protoreflect.Message { // Deprecated: Use VllmAdapterConfig.ProtoReflect.Descriptor instead. func (*VllmAdapterConfig) Descriptor() ([]byte, []int) { - return file_proto_iop_runtime_proto_rawDescGZIP(), []int{19} + return file_proto_iop_runtime_proto_rawDescGZIP(), []int{20} } func (x *VllmAdapterConfig) GetEndpoint() string { @@ -1740,7 +1824,7 @@ type NodeRuntimeConfig struct { func (x *NodeRuntimeConfig) Reset() { *x = NodeRuntimeConfig{} - mi := &file_proto_iop_runtime_proto_msgTypes[20] + mi := &file_proto_iop_runtime_proto_msgTypes[21] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1752,7 +1836,7 @@ func (x *NodeRuntimeConfig) String() string { func (*NodeRuntimeConfig) ProtoMessage() {} func (x *NodeRuntimeConfig) ProtoReflect() protoreflect.Message { - mi := &file_proto_iop_runtime_proto_msgTypes[20] + mi := &file_proto_iop_runtime_proto_msgTypes[21] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1765,7 +1849,7 @@ func (x *NodeRuntimeConfig) ProtoReflect() protoreflect.Message { // Deprecated: Use NodeRuntimeConfig.ProtoReflect.Descriptor instead. func (*NodeRuntimeConfig) Descriptor() ([]byte, []int) { - return file_proto_iop_runtime_proto_rawDescGZIP(), []int{20} + return file_proto_iop_runtime_proto_rawDescGZIP(), []int{21} } func (x *NodeRuntimeConfig) GetConcurrency() int32 { @@ -1867,7 +1951,7 @@ const file_proto_iop_runtime_proto_rawDesc = "" + "\bmetadata\x18\a \x03(\v2%.iop.NodeCommandRequest.MetadataEntryR\bmetadata\x1a;\n" + "\rMetadataEntry\x12\x10\n" + "\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" + - "\x05value\x18\x02 \x01(\tR\x05value:\x028\x01\"\xf8\x02\n" + + "\x05value\x18\x02 \x01(\tR\x05value:\x028\x01\"\xbe\x03\n" + "\x13NodeCommandResponse\x12\x1d\n" + "\n" + "request_id\x18\x01 \x01(\tR\trequestId\x12(\n" + @@ -1878,10 +1962,17 @@ const file_proto_iop_runtime_proto_rawDesc = "" + "session_id\x18\x05 \x01(\tR\tsessionId\x128\n" + "\fusage_status\x18\x06 \x01(\v2\x15.iop.AgentUsageStatusR\vusageStatus\x12\x14\n" + "\x05error\x18\a \x01(\tR\x05error\x12<\n" + - "\x06result\x18\b \x03(\v2$.iop.NodeCommandResponse.ResultEntryR\x06result\x1a9\n" + + "\x06result\x18\b \x03(\v2$.iop.NodeCommandResponse.ResultEntryR\x06result\x12D\n" + + "\x12provider_snapshots\x18\t \x03(\v2\x15.iop.ProviderSnapshotR\x11providerSnapshots\x1a9\n" + "\vResultEntry\x12\x10\n" + "\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" + - "\x05value\x18\x02 \x01(\tR\x05value:\x028\x01\"\xc9\x02\n" + + "\x05value\x18\x02 \x01(\tR\x05value:\x028\x01\"\x95\x01\n" + + "\x10ProviderSnapshot\x12\x18\n" + + "\aadapter\x18\x01 \x01(\tR\aadapter\x12\x16\n" + + "\x06status\x18\x02 \x01(\tR\x06status\x12\x1a\n" + + "\bcapacity\x18\x03 \x01(\x05R\bcapacity\x12\x1b\n" + + "\tin_flight\x18\x04 \x01(\x05R\binFlight\x12\x16\n" + + "\x06queued\x18\x05 \x01(\x05R\x06queued\"\xc9\x02\n" + "\x10AgentUsageStatus\x12\x1d\n" + "\n" + "raw_output\x18\x01 \x01(\tR\trawOutput\x12\x1f\n" + @@ -1981,7 +2072,7 @@ func file_proto_iop_runtime_proto_rawDescGZIP() []byte { } var file_proto_iop_runtime_proto_enumTypes = make([]protoimpl.EnumInfo, 3) -var file_proto_iop_runtime_proto_msgTypes = make([]protoimpl.MessageInfo, 28) +var file_proto_iop_runtime_proto_msgTypes = make([]protoimpl.MessageInfo, 29) var file_proto_iop_runtime_proto_goTypes = []any{ (RunSessionMode)(0), // 0: iop.RunSessionMode (CancelAction)(0), // 1: iop.CancelAction @@ -1994,59 +2085,61 @@ var file_proto_iop_runtime_proto_goTypes = []any{ (*CancelRequest)(nil), // 8: iop.CancelRequest (*NodeCommandRequest)(nil), // 9: iop.NodeCommandRequest (*NodeCommandResponse)(nil), // 10: iop.NodeCommandResponse - (*AgentUsageStatus)(nil), // 11: iop.AgentUsageStatus - (*Error)(nil), // 12: iop.Error - (*RegisterRequest)(nil), // 13: iop.RegisterRequest - (*RegisterResponse)(nil), // 14: iop.RegisterResponse - (*NodeConfigPayload)(nil), // 15: iop.NodeConfigPayload - (*AdapterConfig)(nil), // 16: iop.AdapterConfig - (*MockAdapterConfig)(nil), // 17: iop.MockAdapterConfig - (*CLIAdapterConfig)(nil), // 18: iop.CLIAdapterConfig - (*CLIProfileConfig)(nil), // 19: iop.CLIProfileConfig - (*CLICompletionMarker)(nil), // 20: iop.CLICompletionMarker - (*OllamaAdapterConfig)(nil), // 21: iop.OllamaAdapterConfig - (*VllmAdapterConfig)(nil), // 22: iop.VllmAdapterConfig - (*NodeRuntimeConfig)(nil), // 23: iop.NodeRuntimeConfig - nil, // 24: iop.RunRequest.MetadataEntry - nil, // 25: iop.RunEvent.MetadataEntry - nil, // 26: iop.EdgeNodeEvent.MetadataEntry - nil, // 27: iop.NodeCommandRequest.MetadataEntry - nil, // 28: iop.NodeCommandResponse.ResultEntry - nil, // 29: iop.AgentUsageStatus.MetadataEntry - nil, // 30: iop.CLIAdapterConfig.ProfilesEntry - (*structpb.Struct)(nil), // 31: google.protobuf.Struct + (*ProviderSnapshot)(nil), // 11: iop.ProviderSnapshot + (*AgentUsageStatus)(nil), // 12: iop.AgentUsageStatus + (*Error)(nil), // 13: iop.Error + (*RegisterRequest)(nil), // 14: iop.RegisterRequest + (*RegisterResponse)(nil), // 15: iop.RegisterResponse + (*NodeConfigPayload)(nil), // 16: iop.NodeConfigPayload + (*AdapterConfig)(nil), // 17: iop.AdapterConfig + (*MockAdapterConfig)(nil), // 18: iop.MockAdapterConfig + (*CLIAdapterConfig)(nil), // 19: iop.CLIAdapterConfig + (*CLIProfileConfig)(nil), // 20: iop.CLIProfileConfig + (*CLICompletionMarker)(nil), // 21: iop.CLICompletionMarker + (*OllamaAdapterConfig)(nil), // 22: iop.OllamaAdapterConfig + (*VllmAdapterConfig)(nil), // 23: iop.VllmAdapterConfig + (*NodeRuntimeConfig)(nil), // 24: iop.NodeRuntimeConfig + nil, // 25: iop.RunRequest.MetadataEntry + nil, // 26: iop.RunEvent.MetadataEntry + nil, // 27: iop.EdgeNodeEvent.MetadataEntry + nil, // 28: iop.NodeCommandRequest.MetadataEntry + nil, // 29: iop.NodeCommandResponse.ResultEntry + nil, // 30: iop.AgentUsageStatus.MetadataEntry + nil, // 31: iop.CLIAdapterConfig.ProfilesEntry + (*structpb.Struct)(nil), // 32: google.protobuf.Struct } var file_proto_iop_runtime_proto_depIdxs = []int32{ - 31, // 0: iop.RunRequest.policy:type_name -> google.protobuf.Struct - 31, // 1: iop.RunRequest.input:type_name -> google.protobuf.Struct - 24, // 2: iop.RunRequest.metadata:type_name -> iop.RunRequest.MetadataEntry + 32, // 0: iop.RunRequest.policy:type_name -> google.protobuf.Struct + 32, // 1: iop.RunRequest.input:type_name -> google.protobuf.Struct + 25, // 2: iop.RunRequest.metadata:type_name -> iop.RunRequest.MetadataEntry 0, // 3: iop.RunRequest.session_mode:type_name -> iop.RunSessionMode 6, // 4: iop.RunEvent.usage:type_name -> iop.Usage - 25, // 5: iop.RunEvent.metadata:type_name -> iop.RunEvent.MetadataEntry - 26, // 6: iop.EdgeNodeEvent.metadata:type_name -> iop.EdgeNodeEvent.MetadataEntry + 26, // 5: iop.RunEvent.metadata:type_name -> iop.RunEvent.MetadataEntry + 27, // 6: iop.EdgeNodeEvent.metadata:type_name -> iop.EdgeNodeEvent.MetadataEntry 1, // 7: iop.CancelRequest.action:type_name -> iop.CancelAction 2, // 8: iop.NodeCommandRequest.type:type_name -> iop.NodeCommandType - 27, // 9: iop.NodeCommandRequest.metadata:type_name -> iop.NodeCommandRequest.MetadataEntry + 28, // 9: iop.NodeCommandRequest.metadata:type_name -> iop.NodeCommandRequest.MetadataEntry 2, // 10: iop.NodeCommandResponse.type:type_name -> iop.NodeCommandType - 11, // 11: iop.NodeCommandResponse.usage_status:type_name -> iop.AgentUsageStatus - 28, // 12: iop.NodeCommandResponse.result:type_name -> iop.NodeCommandResponse.ResultEntry - 29, // 13: iop.AgentUsageStatus.metadata:type_name -> iop.AgentUsageStatus.MetadataEntry - 15, // 14: iop.RegisterResponse.config:type_name -> iop.NodeConfigPayload - 16, // 15: iop.NodeConfigPayload.adapters:type_name -> iop.AdapterConfig - 23, // 16: iop.NodeConfigPayload.runtime:type_name -> iop.NodeRuntimeConfig - 31, // 17: iop.AdapterConfig.settings:type_name -> google.protobuf.Struct - 18, // 18: iop.AdapterConfig.cli:type_name -> iop.CLIAdapterConfig - 21, // 19: iop.AdapterConfig.ollama:type_name -> iop.OllamaAdapterConfig - 22, // 20: iop.AdapterConfig.vllm:type_name -> iop.VllmAdapterConfig - 17, // 21: iop.AdapterConfig.mock:type_name -> iop.MockAdapterConfig - 30, // 22: iop.CLIAdapterConfig.profiles:type_name -> iop.CLIAdapterConfig.ProfilesEntry - 20, // 23: iop.CLIProfileConfig.completion_marker:type_name -> iop.CLICompletionMarker - 19, // 24: iop.CLIAdapterConfig.ProfilesEntry.value:type_name -> iop.CLIProfileConfig - 25, // [25:25] is the sub-list for method output_type - 25, // [25:25] is the sub-list for method input_type - 25, // [25:25] is the sub-list for extension type_name - 25, // [25:25] is the sub-list for extension extendee - 0, // [0:25] is the sub-list for field type_name + 12, // 11: iop.NodeCommandResponse.usage_status:type_name -> iop.AgentUsageStatus + 29, // 12: iop.NodeCommandResponse.result:type_name -> iop.NodeCommandResponse.ResultEntry + 11, // 13: iop.NodeCommandResponse.provider_snapshots:type_name -> iop.ProviderSnapshot + 30, // 14: iop.AgentUsageStatus.metadata:type_name -> iop.AgentUsageStatus.MetadataEntry + 16, // 15: iop.RegisterResponse.config:type_name -> iop.NodeConfigPayload + 17, // 16: iop.NodeConfigPayload.adapters:type_name -> iop.AdapterConfig + 24, // 17: iop.NodeConfigPayload.runtime:type_name -> iop.NodeRuntimeConfig + 32, // 18: iop.AdapterConfig.settings:type_name -> google.protobuf.Struct + 19, // 19: iop.AdapterConfig.cli:type_name -> iop.CLIAdapterConfig + 22, // 20: iop.AdapterConfig.ollama:type_name -> iop.OllamaAdapterConfig + 23, // 21: iop.AdapterConfig.vllm:type_name -> iop.VllmAdapterConfig + 18, // 22: iop.AdapterConfig.mock:type_name -> iop.MockAdapterConfig + 31, // 23: iop.CLIAdapterConfig.profiles:type_name -> iop.CLIAdapterConfig.ProfilesEntry + 21, // 24: iop.CLIProfileConfig.completion_marker:type_name -> iop.CLICompletionMarker + 20, // 25: iop.CLIAdapterConfig.ProfilesEntry.value:type_name -> iop.CLIProfileConfig + 26, // [26:26] is the sub-list for method output_type + 26, // [26:26] is the sub-list for method input_type + 26, // [26:26] is the sub-list for extension type_name + 26, // [26:26] is the sub-list for extension extendee + 0, // [0:26] is the sub-list for field type_name } func init() { file_proto_iop_runtime_proto_init() } @@ -2054,7 +2147,7 @@ func file_proto_iop_runtime_proto_init() { if File_proto_iop_runtime_proto != nil { return } - file_proto_iop_runtime_proto_msgTypes[13].OneofWrappers = []any{ + file_proto_iop_runtime_proto_msgTypes[14].OneofWrappers = []any{ (*AdapterConfig_Cli)(nil), (*AdapterConfig_Ollama)(nil), (*AdapterConfig_Vllm)(nil), @@ -2066,7 +2159,7 @@ func file_proto_iop_runtime_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_proto_iop_runtime_proto_rawDesc), len(file_proto_iop_runtime_proto_rawDesc)), NumEnums: 3, - NumMessages: 28, + NumMessages: 29, NumExtensions: 0, NumServices: 0, }, diff --git a/proto/iop/control.proto b/proto/iop/control.proto index a1b2d24..6d17cf7 100644 --- a/proto/iop/control.proto +++ b/proto/iop/control.proto @@ -80,6 +80,7 @@ message EdgeNodeSnapshot { string label = 3; bool connected = 4; NodeConfigPayload config = 5; + repeated ProviderSnapshot provider_snapshots = 6; } // EdgeStatusResponse returns the Edge-owned node snapshot list in reply to an diff --git a/proto/iop/runtime.proto b/proto/iop/runtime.proto index 4231356..3c1b659 100644 --- a/proto/iop/runtime.proto +++ b/proto/iop/runtime.proto @@ -112,6 +112,15 @@ message NodeCommandResponse { // (capabilities, session_list, transport_status). usage_status keeps its // dedicated typed payload. map result = 8; + repeated ProviderSnapshot provider_snapshots = 9; +} + +message ProviderSnapshot { + string adapter = 1; + string status = 2; // unknown|available|unavailable + int32 capacity = 3; + int32 in_flight = 4; + int32 queued = 5; } message AgentUsageStatus {