@av-pi-studio/client 0.0.112 → 0.0.114
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.
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { AgentTodosSnapshot } from "@av-pi-studio/protocol";
|
|
2
|
+
/**
|
|
3
|
+
* Framework-free `agent_todos_*` state (`swe/features/agent-todos.md` § SDK, sprint-079/task-008).
|
|
4
|
+
* No DOM, no I/O, no timers — mirrors `agent-ui-state.ts`'s pure-module convention.
|
|
5
|
+
*
|
|
6
|
+
* `seq` is per-agent, strictly increasing within one daemon process lifetime, and restarts at 1
|
|
7
|
+
* after a daemon restart. This module tracks the last-applied seq inline on the stored snapshot
|
|
8
|
+
* (there is no separate `lastSeq` field — `AgentTodosState` is exactly `{byAgent}`, per the task
|
|
9
|
+
* spec) and drops anything `seq <= lastSeq`. `resetAgentTodosSeq` lowers every stored seq to
|
|
10
|
+
* `-Infinity` so the *next* snapshot of any seq (including a fresh daemon's `1`) always wins,
|
|
11
|
+
* without discarding the previously-rendered tasks in the meantime. The consumer (sprint-080's
|
|
12
|
+
* store) calls it on every reconnect (new `server_info`) and immediately refetches via `list`, so
|
|
13
|
+
* the `-Infinity` seq is never itself rendered.
|
|
14
|
+
*/
|
|
15
|
+
export interface AgentTodosState {
|
|
16
|
+
byAgent: Record<string, AgentTodosSnapshot>;
|
|
17
|
+
}
|
|
18
|
+
export declare const initialAgentTodosState: AgentTodosState;
|
|
19
|
+
/**
|
|
20
|
+
* Last-value-wins per agent. Ignores `seq <= lastSeq` for that agent (stale or duplicate
|
|
21
|
+
* delivery) and returns the **same** `state` object when ignored, so a React selector keyed off
|
|
22
|
+
* `state` reference never re-renders for a dropped push. A `list` response is applied through
|
|
23
|
+
* this same function — there is no separate code path for it.
|
|
24
|
+
*/
|
|
25
|
+
export declare function applyAgentTodosSnapshot(state: AgentTodosState, snapshot: AgentTodosSnapshot): AgentTodosState;
|
|
26
|
+
/**
|
|
27
|
+
* Keeps every agent's last-known snapshot (tasks stay visible) but lowers its tracked seq to
|
|
28
|
+
* `-Infinity`, so the next `applyAgentTodosSnapshot` call for that agent wins regardless of its
|
|
29
|
+
* seq value. Call on every reconnect (new `server_info`), before refetching via `list`.
|
|
30
|
+
*/
|
|
31
|
+
export declare function resetAgentTodosSeq(state: AgentTodosState): AgentTodosState;
|
|
32
|
+
/** The last-applied snapshot for one agent, or `undefined` if none has arrived yet. */
|
|
33
|
+
export declare function snapshotFor(state: AgentTodosState, agentId: string): AgentTodosSnapshot | undefined;
|
|
34
|
+
//# sourceMappingURL=agent-todos-state.d.ts.map
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
export const initialAgentTodosState = { byAgent: {} };
|
|
2
|
+
// ─── Reducer ──────────────────────────────────────────────────────────────────
|
|
3
|
+
/**
|
|
4
|
+
* Last-value-wins per agent. Ignores `seq <= lastSeq` for that agent (stale or duplicate
|
|
5
|
+
* delivery) and returns the **same** `state` object when ignored, so a React selector keyed off
|
|
6
|
+
* `state` reference never re-renders for a dropped push. A `list` response is applied through
|
|
7
|
+
* this same function — there is no separate code path for it.
|
|
8
|
+
*/
|
|
9
|
+
export function applyAgentTodosSnapshot(state, snapshot) {
|
|
10
|
+
const current = state.byAgent[snapshot.agentId];
|
|
11
|
+
if (current && snapshot.seq <= current.seq)
|
|
12
|
+
return state;
|
|
13
|
+
return { byAgent: { ...state.byAgent, [snapshot.agentId]: snapshot } };
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Keeps every agent's last-known snapshot (tasks stay visible) but lowers its tracked seq to
|
|
17
|
+
* `-Infinity`, so the next `applyAgentTodosSnapshot` call for that agent wins regardless of its
|
|
18
|
+
* seq value. Call on every reconnect (new `server_info`), before refetching via `list`.
|
|
19
|
+
*/
|
|
20
|
+
export function resetAgentTodosSeq(state) {
|
|
21
|
+
const byAgent = {};
|
|
22
|
+
for (const [agentId, snapshot] of Object.entries(state.byAgent)) {
|
|
23
|
+
byAgent[agentId] = { ...snapshot, seq: Number.NEGATIVE_INFINITY };
|
|
24
|
+
}
|
|
25
|
+
return { byAgent };
|
|
26
|
+
}
|
|
27
|
+
// ─── Selectors ────────────────────────────────────────────────────────────────
|
|
28
|
+
/** The last-applied snapshot for one agent, or `undefined` if none has arrived yet. */
|
|
29
|
+
export function snapshotFor(state, agentId) {
|
|
30
|
+
return state.byAgent[agentId];
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=agent-todos-state.js.map
|
package/dist/index.d.ts
CHANGED
|
@@ -7,6 +7,7 @@ export * from "./pairing.js";
|
|
|
7
7
|
export * from "./daemon-client.js";
|
|
8
8
|
export * from "./pistudio-client.js";
|
|
9
9
|
export * from "./agent-ui-state.js";
|
|
10
|
+
export * from "./agent-todos-state.js";
|
|
10
11
|
export * from "./agent-ui-controller.js";
|
|
11
12
|
export * from "./terminal-stream-router.js";
|
|
12
13
|
export * from "./file-transfer-client.js";
|
package/dist/index.js
CHANGED
|
@@ -15,6 +15,8 @@ export * from "./daemon-client.js";
|
|
|
15
15
|
export * from "./pistudio-client.js";
|
|
16
16
|
// Extension-UI pure state module (reducer/selectors, sprint-067).
|
|
17
17
|
export * from "./agent-ui-state.js";
|
|
18
|
+
// Agent Todos pure state module (reducer/selectors, sprint-079).
|
|
19
|
+
export * from "./agent-todos-state.js";
|
|
18
20
|
// Extension-UI controller (subscribe-then-list, reconnect resync, agent pruning, sprint-067).
|
|
19
21
|
export * from "./agent-ui-controller.js";
|
|
20
22
|
// Terminal-stream router (binary frame demux per slot).
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { AgentCloneResponse, AgentCompactResponse, AgentCycleModelResponse, AgentExportHtmlResponse, AgentForkMessagesResponse, AgentForkResponse, AgentLastAssistantTextResponse, AgentListCommandsResponse, AgentNewSessionResponse, AgentSessionStatsResponse, AgentSetModelResponse, AgentSetSessionNameResponse, AgentSetThinkingResponse, AgentStreamEvent, AgentSwitchSessionResponse, AgentThinkingLevelsResponse, AgentUiPendingRequest, AgentUiRequest, AgentUiResolved, AgentUiResponse, AgentUiSurface, CreateAgentRequest, ExtensionPacksListResponse, ExtensionPacksSetResponse, FetchAgentTimelineResponse, ProviderAuthInfo, ProviderAuthType, SessionMessage, SkillsInstallResponse, SkillsListResponse, SkillsPreviewResponse, SkillsRemoveResponse, SkillsSetEnabledResponse, SkillsUpdateAllResponse, TimelineDirection, ViewerSettings, ViewerSettingsPatchEntry } from "@av-pi-studio/protocol";
|
|
1
|
+
import type { AgentCloneResponse, AgentCompactResponse, AgentCycleModelResponse, AgentExportHtmlResponse, AgentForkMessagesResponse, AgentForkResponse, AgentLastAssistantTextResponse, AgentListCommandsResponse, AgentNewSessionResponse, AgentSessionStatsResponse, AgentSetModelResponse, AgentSetSessionNameResponse, AgentSetThinkingResponse, AgentStreamEvent, AgentSwitchSessionResponse, AgentThinkingLevelsResponse, AgentTodosSnapshot, AgentTodosUpdate, AgentUiPendingRequest, AgentUiRequest, AgentUiResolved, AgentUiResponse, AgentUiSurface, CreateAgentRequest, ExtensionPacksListResponse, ExtensionPacksSetResponse, FetchAgentTimelineResponse, ProviderAuthInfo, ProviderAuthType, SessionMessage, SkillsInstallResponse, SkillsListResponse, SkillsPreviewResponse, SkillsRemoveResponse, SkillsSetEnabledResponse, SkillsUpdateAllResponse, TimelineDirection, ViewerSettings, ViewerSettingsPatchEntry } from "@av-pi-studio/protocol";
|
|
2
2
|
import type { DaemonClient } from "./daemon-client.js";
|
|
3
3
|
export { detectSkillSource } from "@av-pi-studio/protocol";
|
|
4
4
|
/**
|
|
@@ -362,6 +362,17 @@ export declare class AgentUiError extends Error {
|
|
|
362
362
|
export declare function isAgentUiRequest(message: unknown): message is AgentUiRequest;
|
|
363
363
|
/** Narrows an inbound session message to the `agent_ui_resolved` broadcast. */
|
|
364
364
|
export declare function isAgentUiResolved(message: unknown): message is AgentUiResolved;
|
|
365
|
+
/**
|
|
366
|
+
* A domain failure reported *in `agent_todos_list_response`* (`{ok:false, error}`) rather than as
|
|
367
|
+
* an `rpc_error` — same hazard `ProviderAuthError`/`AgentUiError` guard against. `code` forwards
|
|
368
|
+
* the daemon's `error` string verbatim (open, not enumerated — only `not_found` is documented).
|
|
369
|
+
*/
|
|
370
|
+
export declare class AgentTodosError extends Error {
|
|
371
|
+
readonly code: string;
|
|
372
|
+
constructor(message: string, code: string);
|
|
373
|
+
}
|
|
374
|
+
/** Narrows an inbound session message to the `agent_todos_update` broadcast. */
|
|
375
|
+
export declare function isAgentTodosUpdate(message: unknown): message is AgentTodosUpdate;
|
|
365
376
|
/** The `agent_archived` lifecycle push (`messages.ts`'s `agentArchivedSchema`). */
|
|
366
377
|
export interface AgentArchivedMessage {
|
|
367
378
|
type: "agent_archived";
|
|
@@ -507,6 +518,22 @@ export declare class PiStudioClient {
|
|
|
507
518
|
* from "does this daemon speak the `agent_ui_*` RPC family at all".
|
|
508
519
|
*/
|
|
509
520
|
extensionUiAvailable(): boolean;
|
|
521
|
+
/** True iff the daemon advertised the `agentTodos` capability in `server_info.features`. An old
|
|
522
|
+
* daemon never gets `listAgentTodos`/`onAgentTodosUpdate` called against it. */
|
|
523
|
+
hasAgentTodosCapability(): boolean;
|
|
524
|
+
/**
|
|
525
|
+
* Reconnect/first-load snapshot for one agent's todo list — `agent_todos_list_request`, the
|
|
526
|
+
* default RPC timeout. The response is flat (`{type, requestId, snapshot}` on success, no
|
|
527
|
+
* `ok`), unlike most other RPCs on this facade: `daemon.request` only unwraps a `payload`
|
|
528
|
+
* field, and this family has none. A domain refusal (`{ok:false, error}`, e.g. unknown
|
|
529
|
+
* `agentId`) **throws** `AgentTodosError` — the caller treats any rejection as "keep what you
|
|
530
|
+
* have" (`agent-todos-state.ts`), so a malformed success (neither `snapshot` nor `ok:false`) is
|
|
531
|
+
* also raised rather than silently returning `undefined`.
|
|
532
|
+
*/
|
|
533
|
+
listAgentTodos(agentId: string): Promise<AgentTodosSnapshot>;
|
|
534
|
+
/** Subscribe to every `agent_todos_update` broadcast (any agent) — raw pushes, undeduped and
|
|
535
|
+
* unfiltered by agent; feed them through `agent-todos-state.ts`'s `applyAgentTodosSnapshot`. */
|
|
536
|
+
onAgentTodosUpdate(handler: (snapshot: AgentTodosSnapshot) => void): () => void;
|
|
510
537
|
/** True iff the daemon advertised the `skills` capability in `server_info.features`. */
|
|
511
538
|
hasSkillsCapability(): boolean;
|
|
512
539
|
/** Every resolved Agent Skill plus every resolved `settings.json` package — `skills_list_request`.
|
package/dist/pistudio-client.js
CHANGED
|
@@ -105,6 +105,24 @@ export function isAgentUiResolved(message) {
|
|
|
105
105
|
typeof m.requestId === "string" &&
|
|
106
106
|
typeof m.agentId === "string");
|
|
107
107
|
}
|
|
108
|
+
/**
|
|
109
|
+
* A domain failure reported *in `agent_todos_list_response`* (`{ok:false, error}`) rather than as
|
|
110
|
+
* an `rpc_error` — same hazard `ProviderAuthError`/`AgentUiError` guard against. `code` forwards
|
|
111
|
+
* the daemon's `error` string verbatim (open, not enumerated — only `not_found` is documented).
|
|
112
|
+
*/
|
|
113
|
+
export class AgentTodosError extends Error {
|
|
114
|
+
code;
|
|
115
|
+
constructor(message, code) {
|
|
116
|
+
super(message);
|
|
117
|
+
this.code = code;
|
|
118
|
+
this.name = "AgentTodosError";
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
/** Narrows an inbound session message to the `agent_todos_update` broadcast. */
|
|
122
|
+
export function isAgentTodosUpdate(message) {
|
|
123
|
+
const m = message;
|
|
124
|
+
return (!!m && m.type === "agent_todos_update" && typeof m.snapshot === "object" && m.snapshot !== null);
|
|
125
|
+
}
|
|
108
126
|
/**
|
|
109
127
|
* Narrows an inbound session message to `agent_archived`. **Not** part of the `agent_ui_*` family —
|
|
110
128
|
* exported so a controller (task-003) can prune retained extension-UI state on archive/delete.
|
|
@@ -494,6 +512,41 @@ export class PiStudioClient {
|
|
|
494
512
|
extensionUiAvailable() {
|
|
495
513
|
return this.daemon.hasFeature("extensionUi");
|
|
496
514
|
}
|
|
515
|
+
// ─── Agent Todos (agent_todos_*, sprint-079) ───────────────────────────────
|
|
516
|
+
/** True iff the daemon advertised the `agentTodos` capability in `server_info.features`. An old
|
|
517
|
+
* daemon never gets `listAgentTodos`/`onAgentTodosUpdate` called against it. */
|
|
518
|
+
hasAgentTodosCapability() {
|
|
519
|
+
return this.daemon.hasFeature("agentTodos");
|
|
520
|
+
}
|
|
521
|
+
/**
|
|
522
|
+
* Reconnect/first-load snapshot for one agent's todo list — `agent_todos_list_request`, the
|
|
523
|
+
* default RPC timeout. The response is flat (`{type, requestId, snapshot}` on success, no
|
|
524
|
+
* `ok`), unlike most other RPCs on this facade: `daemon.request` only unwraps a `payload`
|
|
525
|
+
* field, and this family has none. A domain refusal (`{ok:false, error}`, e.g. unknown
|
|
526
|
+
* `agentId`) **throws** `AgentTodosError` — the caller treats any rejection as "keep what you
|
|
527
|
+
* have" (`agent-todos-state.ts`), so a malformed success (neither `snapshot` nor `ok:false`) is
|
|
528
|
+
* also raised rather than silently returning `undefined`.
|
|
529
|
+
*/
|
|
530
|
+
async listAgentTodos(agentId) {
|
|
531
|
+
const payload = await this.daemon.request("agent_todos_list_request", {
|
|
532
|
+
agentId,
|
|
533
|
+
});
|
|
534
|
+
if (payload.ok === false) {
|
|
535
|
+
throw new AgentTodosError(payload.error ?? "failed to list agent todos", payload.error ?? "unknown");
|
|
536
|
+
}
|
|
537
|
+
if (!payload.snapshot) {
|
|
538
|
+
throw new AgentTodosError("malformed agent_todos_list_response: missing snapshot", "malformed_response");
|
|
539
|
+
}
|
|
540
|
+
return payload.snapshot;
|
|
541
|
+
}
|
|
542
|
+
/** Subscribe to every `agent_todos_update` broadcast (any agent) — raw pushes, undeduped and
|
|
543
|
+
* unfiltered by agent; feed them through `agent-todos-state.ts`'s `applyAgentTodosSnapshot`. */
|
|
544
|
+
onAgentTodosUpdate(handler) {
|
|
545
|
+
return this.daemon.onSessionMessage((msg) => {
|
|
546
|
+
if (isAgentTodosUpdate(msg))
|
|
547
|
+
handler(msg.snapshot);
|
|
548
|
+
});
|
|
549
|
+
}
|
|
497
550
|
// ─── Skills (skills_*, sprint-075) ─────────────────────────────────────────
|
|
498
551
|
/** True iff the daemon advertised the `skills` capability in `server_info.features`. */
|
|
499
552
|
hasSkillsCapability() {
|
|
@@ -47,10 +47,11 @@ export function makeScriptedDaemon(opts) {
|
|
|
47
47
|
switch (msg.type) {
|
|
48
48
|
case "agent_ui_respond_request":
|
|
49
49
|
case "agent_ui_list_request":
|
|
50
|
+
case "agent_todos_list_request":
|
|
50
51
|
// Driven manually by each test via `fake.push()` — the ok/error mix (and, for
|
|
51
|
-
// agent_ui_list_request, the exact moment the reply lands
|
|
52
|
-
// events) is exactly what the acceptance criteria
|
|
53
|
-
// would hide it.
|
|
52
|
+
// agent_ui_list_request/agent_todos_list_request, the exact moment the reply lands
|
|
53
|
+
// relative to other pushed events) is exactly what the acceptance criteria
|
|
54
|
+
// differentiate on; an automatic reply would hide it.
|
|
54
55
|
return;
|
|
55
56
|
case "create_agent_request": {
|
|
56
57
|
const agentId = `agent-${++agentSeq}`;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@av-pi-studio/client",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.114",
|
|
4
4
|
"files": [
|
|
5
5
|
"dist",
|
|
6
6
|
"!dist/**/*.map",
|
|
@@ -20,7 +20,12 @@
|
|
|
20
20
|
"clean": "rm -rf dist *.tsbuildinfo"
|
|
21
21
|
},
|
|
22
22
|
"dependencies": {
|
|
23
|
-
"@av-pi-studio/protocol": "^0.0.
|
|
24
|
-
"@av-pi-studio/relay": "^0.0.
|
|
23
|
+
"@av-pi-studio/protocol": "^0.0.114",
|
|
24
|
+
"@av-pi-studio/relay": "^0.0.114"
|
|
25
|
+
},
|
|
26
|
+
"repository": {
|
|
27
|
+
"type": "git",
|
|
28
|
+
"url": "git+https://github.com/avatsaev/pi-studio.git",
|
|
29
|
+
"directory": "packages/client"
|
|
25
30
|
}
|
|
26
31
|
}
|