dsh-cindy-host 0.1.27 → 0.1.29
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/CHANGELOG.md +14 -0
- package/README.md +11 -3
- package/package.json +1 -1
- package/src/host-input-delivery.js +26 -5
- package/src/host-version.js +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.1.29 — 2026-10-29
|
|
4
|
+
|
|
5
|
+
- Publish from a clean, committed Git source tree; preserve the accepted 0.1.28 runtime behavior.
|
|
6
|
+
- Make native Android queue/ACK and durable-outbox regression tests offline and repository-contained.
|
|
7
|
+
- Pin upstream Cindy test sources with Apache-2.0 LICENSE and NOTICE; exclude all fixtures from the npm package.
|
|
8
|
+
- Synchronize the dependency lock and version-aware isolated smoke helper; retain current DSH SDK peers without forced upgrades.
|
|
9
|
+
|
|
10
|
+
## 0.1.28 — 2026-10-28
|
|
11
|
+
|
|
12
|
+
- Fix busy input confirmation on both controllers: DSH admission no longer removes an actual pending inbox row from later projections.
|
|
13
|
+
- Reconcile queued, next-step steering, claimed and accepted states using real inbox/user evidence; never infer consumption from a lagging mirror.
|
|
14
|
+
- Preserve pendingQueue across an Android push overtaking its enqueue ACK; remove it only on genuine claim/consumption/cancellation.
|
|
15
|
+
- Test current Android projection parsing and authority epochs, alongside the existing durable delivery regressions.
|
|
16
|
+
|
|
3
17
|
## 0.1.27 — 2026-10-28
|
|
4
18
|
|
|
5
19
|
- Implements current input-delivery v1 in Host: transactional complete-payload ownership before ACK, receipts, cancellation seals, authoritative clear boundaries, immutable logical identity and recoverable edits.
|
package/README.md
CHANGED
|
@@ -4,15 +4,23 @@ A Host-side adapter connecting current Cindy clients to DeepSeek Harness.
|
|
|
4
4
|
|
|
5
5
|
## Requirements and installation
|
|
6
6
|
|
|
7
|
-
Version 0.1.
|
|
7
|
+
Version 0.1.29 targets the verified **DSH 0.2.1-rc.3** service contracts, with **Node.js >=22.16**. Its DSH peer versions are pinned to this carrier to prevent an installer from mixing a newer prerelease SDK into the running host. The verified Desktop runtime includes Node.js 24.21. Install through the Desktop DSH plugin manager. CLI-managed profiles can use:
|
|
8
8
|
|
|
9
9
|
```sh
|
|
10
|
-
dsh plugin --profile <profile> add dsh-cindy-host@0.1.
|
|
10
|
+
dsh plugin --profile <profile> add dsh-cindy-host@0.1.29
|
|
11
11
|
```
|
|
12
12
|
|
|
13
13
|
Reload the Host plugin after installation. A full DSH restart is separate. Keep existing Cindy sign-in, device identity and settings; do not run two Host processes with the same identity.
|
|
14
14
|
|
|
15
|
-
## 0.1.
|
|
15
|
+
## 0.1.29: reproducible repository release
|
|
16
|
+
|
|
17
|
+
Runtime behavior is unchanged from the phone- and desktop-accepted 0.1.28. This release synchronizes the verified source, dependency lock and offline native-client contract fixtures into Git. The pinned third-party fixtures are test-only and excluded from the npm package.
|
|
18
|
+
|
|
19
|
+
## 0.1.28: busy enqueue confirmation
|
|
20
|
+
|
|
21
|
+
A real DSH pending inbox occurrence remains in pendingQueue (or steeringQueueClientIds) in enqueue replies, pushes and polling until it is actually claimed or committed. Handing input to DSH is not delivery to the user turn. This fixes 0.1.27 empty follow-up projections overwriting the busy-queue acknowledgment on Android and desktop. Regression tests execute the current Android parser and stale-ACK authority handling, not just the durable outbox state machine.
|
|
22
|
+
|
|
23
|
+
## Current durable input delivery
|
|
16
24
|
|
|
17
25
|
- Implements input-delivery v1, not a legacy history-only fallback. Complete text and attachment bytes are held in a private, FULL-synchronous SQLite ledger **before enqueue acknowledgment**.
|
|
18
26
|
- Provides per-clientId pending/accepted/removed/unknown receipts and real chronological around/around-client-id history. Accepted means a real user event was flushed and verified through public persistent storage, not an empty inbox or an in-memory event.
|
package/package.json
CHANGED
|
@@ -17,19 +17,40 @@ function identity(sessionId,clientId){if(typeof sessionId!=='string'||!sessionId
|
|
|
17
17
|
function requestShape(row){return Object.fromEntries(['text','persistedContent','files','agentReferences','sessionRefs','origin','sourcePlugin','createOpts'].filter(k=>row[k]!==undefined).map(k=>[k,row[k]]));}
|
|
18
18
|
/** One owner for durable handoff, session command serialization, and permission to retry admission. */
|
|
19
19
|
export function createInputDeliveryCoordinator({store,backend,baseProjection=()=>({pendingQueue:[],steeringQueueClientIds:[]}),onChange=()=>{},onError=()=>{},validateProjection=()=>true,now=()=>Date.now()}){
|
|
20
|
-
const lanes=new Map(), scheduled=new Set();let stopped=false;
|
|
20
|
+
const lanes=new Map(), scheduled=new Set(), runningBySession=new Map();let stopped=false;
|
|
21
21
|
function notify(sid){queueMicrotask(()=>Promise.resolve().then(()=>onChange(sid)).catch(onError));}
|
|
22
22
|
function lane(sid,fn){const previous=lanes.get(sid)??Promise.resolve();const result=previous.catch(()=>{}).then(fn);const tail=result.catch(()=>{}).finally(()=>{if(lanes.get(sid)===tail)lanes.delete(sid);});lanes.set(sid,tail);return result;}
|
|
23
23
|
function save(entry){store.transaction(()=>store.put(entry));}
|
|
24
24
|
function view(sid,ids=[],override){
|
|
25
|
-
const base=
|
|
26
|
-
|
|
27
|
-
|
|
25
|
+
const base={...baseProjection(sid),...override};
|
|
26
|
+
const entries=store.list(sid);const owned=new Map(entries.map(e=>[e.clientId,e]));
|
|
27
|
+
const isSteering=e=>e.phase==='DSH-pending'&&e.placement==='next-step';
|
|
28
|
+
const pending=(base.pendingQueue??[]).filter(row=>{const e=owned.get(row.clientId);return !e||(!terminal(e.phase)&&e.phase!=='claimed'&&!isSteering(e));});
|
|
29
|
+
const steering=new Set((base.steeringQueueClientIds??[]).filter(cid=>{const e=owned.get(cid);return !e||isSteering(e);}));
|
|
30
|
+
// Admission into DSH is not consumption: keep the real pending occurrence visible
|
|
31
|
+
// even before the independent control-stream mirror catches up.
|
|
32
|
+
for(const e of entries){
|
|
33
|
+
if(isSteering(e)){steering.add(e.clientId);continue;}
|
|
34
|
+
if((e.phase==='stored'||e.phase==='DSH-pending')&&e.queued){const at=pending.findIndex(p=>p.clientId===e.clientId);if(at<0)pending.push(e.queued);else pending[at]=e.queued;}
|
|
35
|
+
}
|
|
36
|
+
const running=runningBySession.get(sid)??base.turnInProgress;
|
|
37
|
+
return {...base,...(typeof running==='boolean'?{turnInProgress:running}:{}),sessionId:sid,pendingQueue:pending,steeringQueueClientIds:[...steering],inputDeliveryVersion:1,clearBoundaryMs:store.boundary(sid),deliveryReceipts:ids.map(clientId=>{const e=store.get(sid,clientId);return{clientId,state:e?e.phase==='removed'?'removed':e.phase==='accepted'?'accepted':e.payload===undefined?'unknown':'pending':'unknown'};})};
|
|
28
38
|
}
|
|
29
39
|
async function evidence(sid){
|
|
30
40
|
const proof=await backend.evidence(sid);
|
|
31
41
|
if(!proof || !Array.isArray(proof.users) || !Array.isArray(proof.inbox))throw deliveryError('DELIVERY_EVIDENCE_UNAVAILABLE','Durable DSH evidence is unavailable');
|
|
32
|
-
|
|
42
|
+
let changed=false;const held=new Map(proof.inbox.map(item=>[item.clientId,item]));
|
|
43
|
+
store.transaction(()=>{
|
|
44
|
+
for(const user of proof.users){const e=store.get(sid,user.clientId);if(e?.phase==='removed')throw deliveryError('DELIVERY_CANCEL_CONFLICT','A sealed cancellation has a durable user message');if(e&&e.phase!=='accepted'){store.put({...e,phase:'accepted',payload:null,userSeq:user.seq,error:null});changed=true;}}
|
|
45
|
+
for(const e of store.list(sid)){
|
|
46
|
+
if(terminal(e.phase)||e.phase==='stored')continue;
|
|
47
|
+
const item=held.get(e.clientId);
|
|
48
|
+
if(item){const placement=item.placement??'next-turn';if(e.phase!=='DSH-pending'||e.placement!==placement){store.put({...e,phase:'DSH-pending',placement});changed=true;}}
|
|
49
|
+
else if(e.phase==='DSH-pending'){store.put({...e,phase:'claimed',placement:null});changed=true;}
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
if(typeof proof.running==='boolean'){if(runningBySession.get(sid)!==proof.running)changed=true;runningBySession.set(sid,proof.running);}
|
|
53
|
+
if(changed)notify(sid);
|
|
33
54
|
return proof;
|
|
34
55
|
}
|
|
35
56
|
async function runSession(sid){
|
package/src/host-version.js
CHANGED