diff --git a/OpenJibo/NuGet.Config b/OpenJibo/NuGet.Config
index a3f3aea..765346e 100644
--- a/OpenJibo/NuGet.Config
+++ b/OpenJibo/NuGet.Config
@@ -2,5 +2,6 @@
+
diff --git a/OpenJibo/OpenJibo.slnx b/OpenJibo/OpenJibo.slnx
index 2ff0a74..eb24c72 100644
--- a/OpenJibo/OpenJibo.slnx
+++ b/OpenJibo/OpenJibo.slnx
@@ -1,12 +1,13 @@
+
-
+
@@ -28,6 +29,9 @@
+
+
+
diff --git a/OpenJibo/README.md b/OpenJibo/README.md
index 7b0c67a..1af8c17 100644
--- a/OpenJibo/README.md
+++ b/OpenJibo/README.md
@@ -36,7 +36,7 @@ Jibo device -> OpenJibo cloud -> normalized runtime contracts -> capabilities an
The first supported recovery path is enthusiast-friendly, not zero-touch:
```text
-QR Wi-Fi -> controlled router/DNS -> redirect legacy Jibo hosts ->
+QR Wi-Fi -> inject OpenJibo region config -> set robot region ->
RCM/device patch for TLS and host acceptance -> OpenJibo cloud on Azure
```
@@ -47,6 +47,7 @@ That path is documented in [docs/device-bootstrap.md](C:/Projects/JiboExperiment
```text
OpenJibo/
docs/
+ development-plan.md
device-bootstrap.md
protocol-inventory.md
public-site-plan.md
@@ -78,12 +79,14 @@ OpenJibo/
- port required endpoint and WebSocket behavior from Node to .NET
- keep protocol captures and replay fixtures current
- harden device bootstrap documentation and scripts
+- map more endpoints and behaviors beyond the current Node coverage
- stand up the initial `openjibo.com` information site
## Important Docs
- [Cloud overview](/src/Jibo.Cloud/README.md)
+- [Development plan](/docs/development-plan.md)
- [Protocol inventory](/docs/protocol-inventory.md)
- [Support tiers](/docs/support-tiers.md)
- [Device bootstrap path](/docs/device-bootstrap.md)
-- [Public site plan](/docs/public-site-plan.md)
\ No newline at end of file
+- [Public site plan](/docs/public-site-plan.md)
diff --git a/OpenJibo/docs/development-plan.md b/OpenJibo/docs/development-plan.md
new file mode 100644
index 0000000..866a770
--- /dev/null
+++ b/OpenJibo/docs/development-plan.md
@@ -0,0 +1,65 @@
+# Development Plan
+
+## Summary
+
+This document is the working implementation plan after the initial hosted-cloud scaffold.
+
+It is intentionally broader than the current Node server. The Node server is a protocol oracle and discovery tool, not the complete map of Jibo.
+
+## Current Scope
+
+- stable .NET cloud scaffold
+- Azure-oriented architecture and data ownership
+- normalized runtime contracts for cloud-to-runtime handoff
+- bootstrap documentation for region injection and targeted device patching
+- starter endpoint coverage for account, notification, robot, loop, update, uploads, and core WebSocket acceptance
+- starter xUnit coverage for the .NET application layer
+
+## Next Implementation Scope
+
+- expand HTTP `X-Amz-Target` coverage from observed traffic and fixtures
+- grow WebSocket compatibility from stub acceptance into realistic turn orchestration
+- replace in-memory state with Azure SQL-backed persistence
+- add structured fixture replay tests
+- harden region/bootstrap docs by software version
+
+## Discovery Scope
+
+We still need to map more than the current Node server expresses. Priority discovery areas:
+
+- all hostnames and service prefixes observed in real startup and turn traffic
+- skill launch and skill lifecycle flows
+- interactivity command families beyond the current joke flow
+- richer embodied speech and animation behaviors
+- upload, logging, backup, and key-sharing flows
+- per-version configuration differences and region handling
+
+## Speech, Animation, And ESML
+
+The current joke flow is only a small foothold into Jibo expressiveness.
+
+Future work should map:
+
+- direct speech modifiers
+- animation selection and filtering
+- embodied speech behaviors
+- ESML and SSML subsets
+- interactions between speech, visuals, and timing
+
+Useful external references:
+
+- [Speak-Tweak Docs](https://hri2024.jibo.media.mit.edu/Speak-Tweak-Docs)
+- [ESML PDF](https://hri2024.jibo.media.mit.edu/attachments/SDK-SDK---ESML-121023-203758.pdf)
+
+## Future Scope
+
+- full endpoint inventory beyond the current Node mapping
+- OTA-driven recovery
+- paid hosted plans or donation-supported hosting
+- deeper on-device bridge and OS modernization
+- more capable skill/runtime integration
+- possible LLM or tool-use patterns inspired by workshop-era experimentation
+
+## MCP-Like Ideas
+
+Recent MIT workshop materials suggest experimentation around modern AI tooling for Jibo, including an MCP-oriented idea. We should treat that as inspiration for future OpenJibo directions, not as a present dependency or supported integration.
diff --git a/OpenJibo/docs/device-bootstrap.md b/OpenJibo/docs/device-bootstrap.md
index 9fc5e85..a04dc28 100644
--- a/OpenJibo/docs/device-bootstrap.md
+++ b/OpenJibo/docs/device-bootstrap.md
@@ -5,7 +5,7 @@
The first supported OpenJibo recovery path is:
```text
-QR Wi-Fi -> controlled router/DNS -> redirect Jibo hosts ->
+QR Wi-Fi -> inject OpenJibo region config -> set robot region ->
RCM/device patch -> Azure-hosted OpenJibo cloud
```
@@ -13,26 +13,44 @@ This is the path we can document, repeat, and improve.
## Why This Path Comes First
-- it matches what the current Node prototype already requires
+- it matches the region-driven configuration seams observed on the robot
- it keeps the hosted cloud work grounded in real device traffic
- it avoids blocking the entire revival on OTA before cloud compatibility exists
## Bootstrap Checklist
1. Connect the robot to a controlled Wi-Fi network.
-2. Redirect legacy cloud hostnames to the OpenJibo environment.
-3. Prevent fallback DNS from bypassing the controlled resolver.
+2. Add an OpenJibo region entry to `/etc/jibo-jetstream-service.json`.
+3. Set the robot `region` field in `/var/jibo/credentials.json` to the OpenJibo region.
4. Gain RCM/device access for targeted TLS or host validation changes.
5. Verify robot startup, token flow, socket flow, and first-turn behavior.
-## Required Host Routing
+## Region-Driven Configuration
-At minimum, watch and validate:
+Current findings suggest the preferred OpenJibo bootstrap path is to inject a new region configuration rather than override every hostname manually.
+
+Confirmed paths:
+
+- `/etc/jibo-jetstream-service.json`
+ Add an OpenJibo region definition that points Jibo to our cloud.
+- `/var/jibo/credentials.json`
+ Set the robot `region` field to the injected OpenJibo region.
+
+Observed additional region-related files worth documenting and auditing:
+
+- `/etc/jibo-ssm/*.json`
+- `/skills/jibo/Jibo/Skills/@be/be/node_modules/language-subtag-registry/data/json/registry.json`
+- `/skills/jibo/Jibo/Skills/oobe-config/config.json`
+
+These should be treated as configuration discovery targets, not yet as the authoritative complete list.
+
+## Required Hosts
+
+The currently relevant public hostnames for the OpenJibo cloud path are:
- `api.jibo.com`
- `api-socket.jibo.com`
- `neo-hub.jibo.com`
-- `neohub.jibo.com`
## Scripted Helpers
@@ -42,7 +60,7 @@ Bootstrap helper scripts live in [scripts/bootstrap](C:/Projects/JiboExperiments
- `Generate-JiboDnsOverrides.ps1`
- `Test-OpenJiboRouting.ps1`
-These are intentionally conservative helpers for discovery and verification, not destructive patch tools.
+These are intentionally conservative helpers for discovery and verification, not destructive patch tools. They remain useful for controlled-network testing, even though the preferred long-term device path is region injection.
## TLS And Runtime Patching
@@ -53,6 +71,7 @@ Near-term guidance:
- record each patch location by software version
- prefer small, repeatable changes over ad hoc edits
- keep a versioned host inventory and patch checklist
+- keep a versioned region-config checklist
- do not describe OTA as the primary bootstrap method until the hosted cloud is stable
## Smoke Test Goals
diff --git a/OpenJibo/docs/protocol-inventory.md b/OpenJibo/docs/protocol-inventory.md
index 9749b4d..af3f7ad 100644
--- a/OpenJibo/docs/protocol-inventory.md
+++ b/OpenJibo/docs/protocol-inventory.md
@@ -4,6 +4,8 @@
This document tracks the currently observed cloud surface area for Jibo and helps keep the .NET port aligned with real behavior captured by the Node prototype.
+It is not a claim that the current Node server covers all Jibo endpoints or behaviors. It reflects only the portions mapped so far.
+
Confidence levels:
- `high`: observed in code and currently represented in the .NET scaffold
@@ -17,7 +19,20 @@ Confidence levels:
| `api.jibo.com` | HTTPS API target for `X-Amz-Target` operations | high | Main request dispatch path in the Node prototype |
| `api-socket.jibo.com` | token-authenticated WebSocket path | medium | Node accepts tokenized connections and intentionally sends no greeting |
| `neo-hub.jibo.com` | listen and proactive WebSocket traffic | medium | Path-driven split between listen and `/v1/proactive` |
-| `neohub.jibo.com` | likely alias/spelling variant to watch | low | Mentioned in docs; validate against real traffic |
+
+## Region Configuration
+
+Current robot findings suggest the preferred OpenJibo bootstrap path is to inject a new region configuration rather than treat host overrides as the only integration seam.
+
+Confirmed or strongly observed files:
+
+- `/etc/jibo-jetstream-service.json`
+- `/var/jibo/credentials.json`
+- `/etc/jibo-ssm/*.json`
+- `/skills/jibo/Jibo/Skills/@be/be/node_modules/language-subtag-registry/data/json/registry.json`
+- `/skills/jibo/Jibo/Skills/oobe-config/config.json`
+
+The first two are the clearest current OpenJibo injection points. The others should remain on the audit list while endpoint and behavior mapping continues.
## HTTP Dispatch Families
@@ -65,6 +80,22 @@ The first .NET hosted milestone should fully support:
- basic listen/proactive WebSocket acceptance
- normalized turn and reply mapping for simple chat
+## Known Beyond Current Node Coverage
+
+The platform scope is broader than the endpoints currently modeled in `open-jibo-link.js`. Known areas that still need mapping include:
+
+- broader skill launch and lifecycle behavior
+- interactivity command families beyond the joke starter path
+- richer animation and expression control
+- ESML and embodied speech features
+- additional service families and region-specific endpoint behavior
+- startup and configuration differences across Jibo software variants
+
+Useful external references for future mapping:
+
+- [Speak-Tweak Docs](https://hri2024.jibo.media.mit.edu/Speak-Tweak-Docs)
+- [ESML PDF](https://hri2024.jibo.media.mit.edu/attachments/SDK-SDK---ESML-121023-203758.pdf)
+
## Fixture Source
Sanitized fixtures live under [src/Jibo.Cloud/node/fixtures](C:/Projects/JiboExperiments/OpenJibo/src/Jibo.Cloud/node/fixtures) and should be expanded as real traffic is captured.
diff --git a/OpenJibo/scripts/bootstrap/Discover-JiboHosts.ps1 b/OpenJibo/scripts/bootstrap/Discover-JiboHosts.ps1
index eaed8cb..5bf4a1e 100644
--- a/OpenJibo/scripts/bootstrap/Discover-JiboHosts.ps1
+++ b/OpenJibo/scripts/bootstrap/Discover-JiboHosts.ps1
@@ -3,8 +3,7 @@ param(
[string[]]$KnownHosts = @(
"api.jibo.com",
"api-socket.jibo.com",
- "neo-hub.jibo.com",
- "neohub.jibo.com"
+ "neo-hub.jibo.com"
)
)
diff --git a/OpenJibo/scripts/bootstrap/Generate-JiboDnsOverrides.ps1 b/OpenJibo/scripts/bootstrap/Generate-JiboDnsOverrides.ps1
index ef36c2b..dabdfd1 100644
--- a/OpenJibo/scripts/bootstrap/Generate-JiboDnsOverrides.ps1
+++ b/OpenJibo/scripts/bootstrap/Generate-JiboDnsOverrides.ps1
@@ -3,8 +3,7 @@ param(
[string[]]$HostNames = @(
"api.jibo.com",
"api-socket.jibo.com",
- "neo-hub.jibo.com",
- "neohub.jibo.com"
+ "neo-hub.jibo.com"
)
)
diff --git a/OpenJibo/src/Jibo.Cloud/dotnet/src/Jibo.Cloud.Infrastructure/Persistence/InMemoryCloudStateStore.cs b/OpenJibo/src/Jibo.Cloud/dotnet/src/Jibo.Cloud.Infrastructure/Persistence/InMemoryCloudStateStore.cs
index f233170..cedb25b 100644
--- a/OpenJibo/src/Jibo.Cloud/dotnet/src/Jibo.Cloud.Infrastructure/Persistence/InMemoryCloudStateStore.cs
+++ b/OpenJibo/src/Jibo.Cloud/dotnet/src/Jibo.Cloud.Infrastructure/Persistence/InMemoryCloudStateStore.cs
@@ -20,8 +20,7 @@ public sealed class InMemoryCloudStateStore : ICloudStateStore
{
["api.jibo.com"] = "openjibo.com",
["api-socket.jibo.com"] = "openjibo.com",
- ["neo-hub.jibo.com"] = "openjibo.com",
- ["neohub.jibo.com"] = "openjibo.com"
+ ["neo-hub.jibo.com"] = "openjibo.com"
}
};
diff --git a/OpenJibo/tests/Jibo.Cloud.Tests/GlobalUsings.cs b/OpenJibo/tests/Jibo.Cloud.Tests/GlobalUsings.cs
new file mode 100644
index 0000000..c802f44
--- /dev/null
+++ b/OpenJibo/tests/Jibo.Cloud.Tests/GlobalUsings.cs
@@ -0,0 +1 @@
+global using Xunit;
diff --git a/OpenJibo/tests/Jibo.Cloud.Tests/Jibo.Cloud.Tests.csproj b/OpenJibo/tests/Jibo.Cloud.Tests/Jibo.Cloud.Tests.csproj
new file mode 100644
index 0000000..2909fbc
--- /dev/null
+++ b/OpenJibo/tests/Jibo.Cloud.Tests/Jibo.Cloud.Tests.csproj
@@ -0,0 +1,22 @@
+
+
+
+ net10.0
+ enable
+ enable
+ false
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/OpenJibo/tests/Jibo.Cloud.Tests/Protocol/JiboCloudProtocolServiceTests.cs b/OpenJibo/tests/Jibo.Cloud.Tests/Protocol/JiboCloudProtocolServiceTests.cs
new file mode 100644
index 0000000..9432ac3
--- /dev/null
+++ b/OpenJibo/tests/Jibo.Cloud.Tests/Protocol/JiboCloudProtocolServiceTests.cs
@@ -0,0 +1,64 @@
+using System.Text.Json;
+using Jibo.Cloud.Application.Services;
+using Jibo.Cloud.Domain.Models;
+using Jibo.Cloud.Infrastructure.Persistence;
+
+namespace Jibo.Cloud.Tests.Protocol;
+
+public sealed class JiboCloudProtocolServiceTests
+{
+ private readonly JiboCloudProtocolService _service = new(new InMemoryCloudStateStore());
+
+ [Fact]
+ public async Task CreateHubToken_ReturnsTokenAndExpiry()
+ {
+ var result = await _service.DispatchAsync(new ProtocolEnvelope
+ {
+ HostName = "api.jibo.com",
+ Method = "POST",
+ ServicePrefix = "Account_20160715",
+ Operation = "CreateHubToken",
+ BodyText = "{}"
+ });
+
+ using var payload = JsonDocument.Parse(result.BodyText);
+ Assert.Equal(200, result.StatusCode);
+ Assert.StartsWith("hub-", payload.RootElement.GetProperty("token").GetString());
+ Assert.True(payload.RootElement.GetProperty("expires").GetInt64() > 0);
+ }
+
+ [Fact]
+ public async Task NewRobotToken_UsesBodyDeviceId()
+ {
+ var result = await _service.DispatchAsync(new ProtocolEnvelope
+ {
+ HostName = "api.jibo.com",
+ Method = "POST",
+ ServicePrefix = "Notification_20160715",
+ Operation = "NewRobotToken",
+ BodyText = """{"deviceId":"robot-123"}"""
+ });
+
+ using var payload = JsonDocument.Parse(result.BodyText);
+ Assert.Equal(200, result.StatusCode);
+ Assert.Contains("robot-123", payload.RootElement.GetProperty("token").GetString());
+ }
+
+ [Fact]
+ public async Task GetUpdateFrom_ReturnsNoOpUpdate()
+ {
+ var result = await _service.DispatchAsync(new ProtocolEnvelope
+ {
+ HostName = "api.jibo.com",
+ Method = "POST",
+ ServicePrefix = "Update_20160715",
+ Operation = "GetUpdateFrom",
+ BodyText = """{"subsystem":"robot","fromVersion":"1.0.0"}"""
+ });
+
+ using var payload = JsonDocument.Parse(result.BodyText);
+ Assert.Equal(200, result.StatusCode);
+ Assert.Equal("robot", payload.RootElement.GetProperty("subsystem").GetString());
+ Assert.True(payload.RootElement.TryGetProperty("url", out _));
+ }
+}
diff --git a/OpenJibo/tests/Jibo.Cloud.Tests/WebSockets/JiboWebSocketServiceTests.cs b/OpenJibo/tests/Jibo.Cloud.Tests/WebSockets/JiboWebSocketServiceTests.cs
new file mode 100644
index 0000000..861f7f6
--- /dev/null
+++ b/OpenJibo/tests/Jibo.Cloud.Tests/WebSockets/JiboWebSocketServiceTests.cs
@@ -0,0 +1,55 @@
+using System.Text.Json;
+using Jibo.Cloud.Application.Services;
+using Jibo.Cloud.Domain.Models;
+using Jibo.Cloud.Infrastructure.Persistence;
+
+namespace Jibo.Cloud.Tests.WebSockets;
+
+public sealed class JiboWebSocketServiceTests
+{
+ private readonly JiboWebSocketService _service;
+
+ public JiboWebSocketServiceTests()
+ {
+ var store = new InMemoryCloudStateStore();
+ _service = new JiboWebSocketService(
+ store,
+ new ProtocolToTurnContextMapper(),
+ new DemoConversationBroker(),
+ new ResponsePlanToSocketMessagesMapper());
+ }
+
+ [Fact]
+ public async Task ListenMessage_ReturnsResponseAndEos()
+ {
+ var replies = await _service.HandleMessageAsync(new WebSocketMessageEnvelope
+ {
+ HostName = "neo-hub.jibo.com",
+ Path = "/listen",
+ Kind = "neo-hub-listen",
+ Token = "hub-test-token",
+ Text = """{"type":"LISTEN","data":{"text":"hello jibo"}}"""
+ });
+
+ Assert.Equal(2, replies.Count);
+ Assert.Contains("OPENJIBO_RESPONSE", replies[0].Text);
+ Assert.Contains("EOS", replies[1].Text);
+ }
+
+ [Fact]
+ public async Task BinaryMessage_ReturnsAcknowledgementPayload()
+ {
+ var replies = await _service.HandleMessageAsync(new WebSocketMessageEnvelope
+ {
+ HostName = "neo-hub.jibo.com",
+ Path = "/listen",
+ Kind = "neo-hub-listen",
+ Token = "hub-test-token",
+ Binary = [1, 2, 3, 4]
+ });
+
+ using var payload = JsonDocument.Parse(replies[0].Text!);
+ Assert.Equal("OPENJIBO_AUDIO_RECEIVED", payload.RootElement.GetProperty("type").GetString());
+ Assert.Equal(4, payload.RootElement.GetProperty("data").GetProperty("bytes").GetInt32());
+ }
+}