pilotswarm-sdk 0.4.0 → 0.5.0
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/api/src/http-api-transport.js +4 -4
- package/api/src/protocol.js +10 -3
- package/dist/agent-loader.d.ts +2 -0
- package/dist/agent-loader.d.ts.map +1 -1
- package/dist/agent-loader.js +7 -1
- package/dist/agent-loader.js.map +1 -1
- package/dist/blob-store.d.ts +10 -1
- package/dist/blob-store.d.ts.map +1 -1
- package/dist/blob-store.js +275 -13
- package/dist/blob-store.js.map +1 -1
- package/dist/client.d.ts +1 -0
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +5 -1
- package/dist/client.js.map +1 -1
- package/dist/cms-migrations.d.ts.map +1 -1
- package/dist/cms-migrations.js +544 -1
- package/dist/cms-migrations.js.map +1 -1
- package/dist/cms.d.ts +18 -4
- package/dist/cms.d.ts.map +1 -1
- package/dist/cms.js +62 -3
- package/dist/cms.js.map +1 -1
- package/dist/fault-injection.d.ts +2 -0
- package/dist/fault-injection.d.ts.map +1 -0
- package/dist/fault-injection.js +45 -0
- package/dist/fault-injection.js.map +1 -0
- package/dist/management-client.d.ts +6 -2
- package/dist/management-client.d.ts.map +1 -1
- package/dist/management-client.js +8 -4
- package/dist/management-client.js.map +1 -1
- package/dist/orchestration/index.d.ts +3 -3
- package/dist/orchestration/index.js +1 -1
- package/dist/orchestration/lifecycle.d.ts +9 -2
- package/dist/orchestration/lifecycle.d.ts.map +1 -1
- package/dist/orchestration/lifecycle.js +21 -55
- package/dist/orchestration/lifecycle.js.map +1 -1
- package/dist/orchestration/runtime.d.ts +1 -1
- package/dist/orchestration/state.d.ts +7 -3
- package/dist/orchestration/state.d.ts.map +1 -1
- package/dist/orchestration/state.js +13 -4
- package/dist/orchestration/state.js.map +1 -1
- package/dist/orchestration/turn.d.ts.map +1 -1
- package/dist/orchestration/turn.js +98 -66
- package/dist/orchestration/turn.js.map +1 -1
- package/dist/orchestration-registry.d.ts.map +1 -1
- package/dist/orchestration-registry.js +6 -2
- package/dist/orchestration-registry.js.map +1 -1
- package/dist/orchestration-version.d.ts +1 -1
- package/dist/orchestration-version.js +1 -1
- package/dist/orchestration.d.ts +2 -2
- package/dist/orchestration.js +4 -4
- package/dist/orchestration_1_0_46.d.ts +1 -1
- package/dist/orchestration_1_0_47.d.ts +1 -1
- package/dist/orchestration_1_0_48.d.ts +1 -1
- package/dist/orchestration_1_0_49.d.ts +1 -1
- package/dist/orchestration_1_0_50.d.ts +1 -1
- package/dist/orchestration_1_0_56/agents.d.ts +41 -0
- package/dist/orchestration_1_0_56/agents.d.ts.map +1 -0
- package/dist/orchestration_1_0_56/agents.js +758 -0
- package/dist/orchestration_1_0_56/agents.js.map +1 -0
- package/dist/orchestration_1_0_56/index.d.ts +24 -0
- package/dist/orchestration_1_0_56/index.d.ts.map +1 -0
- package/dist/orchestration_1_0_56/index.js +13 -0
- package/dist/orchestration_1_0_56/index.js.map +1 -0
- package/dist/orchestration_1_0_56/lifecycle.d.ts +41 -0
- package/dist/orchestration_1_0_56/lifecycle.d.ts.map +1 -0
- package/dist/orchestration_1_0_56/lifecycle.js +580 -0
- package/dist/orchestration_1_0_56/lifecycle.js.map +1 -0
- package/dist/orchestration_1_0_56/queue.d.ts +7 -0
- package/dist/orchestration_1_0_56/queue.d.ts.map +1 -0
- package/dist/orchestration_1_0_56/queue.js +644 -0
- package/dist/orchestration_1_0_56/queue.js.map +1 -0
- package/dist/orchestration_1_0_56/runtime.d.ts +29 -0
- package/dist/orchestration_1_0_56/runtime.d.ts.map +1 -0
- package/dist/orchestration_1_0_56/runtime.js +194 -0
- package/dist/orchestration_1_0_56/runtime.js.map +1 -0
- package/dist/orchestration_1_0_56/state.d.ts +126 -0
- package/dist/orchestration_1_0_56/state.d.ts.map +1 -0
- package/dist/orchestration_1_0_56/state.js +106 -0
- package/dist/orchestration_1_0_56/state.js.map +1 -0
- package/dist/orchestration_1_0_56/turn.d.ts +30 -0
- package/dist/orchestration_1_0_56/turn.d.ts.map +1 -0
- package/dist/orchestration_1_0_56/turn.js +1075 -0
- package/dist/orchestration_1_0_56/turn.js.map +1 -0
- package/dist/orchestration_1_0_56/utils.d.ts +22 -0
- package/dist/orchestration_1_0_56/utils.d.ts.map +1 -0
- package/dist/orchestration_1_0_56/utils.js +226 -0
- package/dist/orchestration_1_0_56/utils.js.map +1 -0
- package/dist/orchestration_1_0_57/agents.d.ts +41 -0
- package/dist/orchestration_1_0_57/agents.d.ts.map +1 -0
- package/dist/orchestration_1_0_57/agents.js +758 -0
- package/dist/orchestration_1_0_57/agents.js.map +1 -0
- package/dist/orchestration_1_0_57/index.d.ts +24 -0
- package/dist/orchestration_1_0_57/index.d.ts.map +1 -0
- package/dist/orchestration_1_0_57/index.js +13 -0
- package/dist/orchestration_1_0_57/index.js.map +1 -0
- package/dist/orchestration_1_0_57/lifecycle.d.ts +49 -0
- package/dist/orchestration_1_0_57/lifecycle.d.ts.map +1 -0
- package/dist/orchestration_1_0_57/lifecycle.js +583 -0
- package/dist/orchestration_1_0_57/lifecycle.js.map +1 -0
- package/dist/orchestration_1_0_57/queue.d.ts +7 -0
- package/dist/orchestration_1_0_57/queue.d.ts.map +1 -0
- package/dist/orchestration_1_0_57/queue.js +644 -0
- package/dist/orchestration_1_0_57/queue.js.map +1 -0
- package/dist/orchestration_1_0_57/runtime.d.ts +29 -0
- package/dist/orchestration_1_0_57/runtime.d.ts.map +1 -0
- package/dist/orchestration_1_0_57/runtime.js +198 -0
- package/dist/orchestration_1_0_57/runtime.js.map +1 -0
- package/dist/orchestration_1_0_57/state.d.ts +133 -0
- package/dist/orchestration_1_0_57/state.d.ts.map +1 -0
- package/dist/orchestration_1_0_57/state.js +115 -0
- package/dist/orchestration_1_0_57/state.js.map +1 -0
- package/dist/orchestration_1_0_57/turn.d.ts +30 -0
- package/dist/orchestration_1_0_57/turn.d.ts.map +1 -0
- package/dist/orchestration_1_0_57/turn.js +1110 -0
- package/dist/orchestration_1_0_57/turn.js.map +1 -0
- package/dist/orchestration_1_0_57/utils.d.ts +22 -0
- package/dist/orchestration_1_0_57/utils.d.ts.map +1 -0
- package/dist/orchestration_1_0_57/utils.js +226 -0
- package/dist/orchestration_1_0_57/utils.js.map +1 -0
- package/dist/session-lifecycle.d.ts +80 -0
- package/dist/session-lifecycle.d.ts.map +1 -0
- package/dist/session-lifecycle.js +257 -0
- package/dist/session-lifecycle.js.map +1 -0
- package/dist/session-manager.d.ts +21 -0
- package/dist/session-manager.d.ts.map +1 -1
- package/dist/session-manager.js +137 -1
- package/dist/session-manager.js.map +1 -1
- package/dist/session-proxy.d.ts +6 -1
- package/dist/session-proxy.d.ts.map +1 -1
- package/dist/session-proxy.js +1123 -953
- package/dist/session-proxy.js.map +1 -1
- package/dist/session-store.d.ts +48 -3
- package/dist/session-store.d.ts.map +1 -1
- package/dist/session-store.js +394 -28
- package/dist/session-store.js.map +1 -1
- package/dist/snapshot-protocol.d.ts +101 -0
- package/dist/snapshot-protocol.d.ts.map +1 -0
- package/dist/snapshot-protocol.js +120 -0
- package/dist/snapshot-protocol.js.map +1 -0
- package/dist/types.d.ts +26 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/wait-affinity.d.ts +11 -0
- package/dist/wait-affinity.d.ts.map +1 -1
- package/dist/wait-affinity.js +6 -0
- package/dist/wait-affinity.js.map +1 -1
- package/dist/web/web-client.d.ts +1 -0
- package/dist/web/web-client.d.ts.map +1 -1
- package/dist/web/web-client.js +1 -0
- package/dist/web/web-client.js.map +1 -1
- package/dist/web/web-management-client.d.ts +30 -11
- package/dist/web/web-management-client.d.ts.map +1 -1
- package/dist/web/web-management-client.js +34 -19
- package/dist/web/web-management-client.js.map +1 -1
- package/dist/worker.d.ts +16 -1
- package/dist/worker.d.ts.map +1 -1
- package/dist/worker.js +92 -13
- package/dist/worker.js.map +1 -1
- package/package.json +7 -4
- package/plugins/mgmt/agents/agent-tuner.agent.md +6 -0
- package/plugins/mgmt/agents/facts-manager.agent.md +6 -0
- package/plugins/mgmt/agents/pilotswarm.agent.md +6 -0
- package/plugins/mgmt/agents/resourcemgr.agent.md +6 -0
- package/plugins/mgmt/agents/sweeper.agent.md +6 -0
|
@@ -288,12 +288,12 @@ export class HttpApiTransport {
|
|
|
288
288
|
|
|
289
289
|
// ── Events / history ────────────────────────────────────────────────
|
|
290
290
|
|
|
291
|
-
async getSessionEvents(sessionId, afterSeq, limit) {
|
|
292
|
-
return this.api.call("getSessionEvents", { sessionId, afterSeq, limit });
|
|
291
|
+
async getSessionEvents(sessionId, afterSeq, limit, eventTypes) {
|
|
292
|
+
return this.api.call("getSessionEvents", { sessionId, afterSeq, limit, eventTypes });
|
|
293
293
|
}
|
|
294
294
|
|
|
295
|
-
async getSessionEventsBefore(sessionId, beforeSeq, limit) {
|
|
296
|
-
return this.api.call("getSessionEventsBefore", { sessionId, beforeSeq, limit });
|
|
295
|
+
async getSessionEventsBefore(sessionId, beforeSeq, limit, eventTypes) {
|
|
296
|
+
return this.api.call("getSessionEventsBefore", { sessionId, beforeSeq, limit, eventTypes });
|
|
297
297
|
}
|
|
298
298
|
|
|
299
299
|
async getExecutionHistory(sessionId, executionId) {
|
package/api/src/protocol.js
CHANGED
|
@@ -46,7 +46,7 @@ export const OPERATIONS = [
|
|
|
46
46
|
// ── Sessions (client surface) ───────────────────────────────────────
|
|
47
47
|
{ name: "listSessions", method: "GET", path: "/sessions", summary: "List session summaries." },
|
|
48
48
|
{ name: "createSession", method: "POST", path: "/sessions", params: { model: body(), reasoningEffort: body(), groupId: body() }, summary: "Create a session. Owner is the authenticated principal." },
|
|
49
|
-
{ name: "createSessionForAgent", method: "POST", path: "/sessions/for-agent", params: { agentName: body(), model: body(), reasoningEffort: body(), title: body(), splash: body(), initialPrompt: body(), groupId: body() }, summary: "Create a session bound to a named agent." },
|
|
49
|
+
{ name: "createSessionForAgent", method: "POST", path: "/sessions/for-agent", params: { agentName: body(), model: body(), reasoningEffort: body(), title: body(), splash: body(), splashMobile: body(), initialPrompt: body(), groupId: body() }, summary: "Create a session bound to a named agent." },
|
|
50
50
|
{ name: "getSession", method: "GET", path: "/sessions/:sessionId", params: { sessionId: path("sessionId") }, summary: "Get one session view (live orchestration status)." },
|
|
51
51
|
{ name: "deleteSession", method: "DELETE", path: "/sessions/:sessionId", params: { sessionId: path("sessionId") }, summary: "Cancel and soft-delete a session." },
|
|
52
52
|
{ name: "sendMessage", method: "POST", path: "/sessions/:sessionId/messages", params: { sessionId: path("sessionId"), prompt: body(), options: body() }, summary: "Send a prompt (options: { enqueueOnly?, clientMessageIds? })." },
|
|
@@ -75,8 +75,8 @@ export const OPERATIONS = [
|
|
|
75
75
|
{ name: "getLatestResponse", method: "GET", path: "/management/sessions/:sessionId/latest-response", params: { sessionId: path("sessionId") }, summary: "Latest turn response payload, if any." },
|
|
76
76
|
{ name: "getOrchestrationStats", method: "GET", path: "/management/sessions/:sessionId/orchestration-stats", params: { sessionId: path("sessionId") }, summary: "Orchestration runtime stats." },
|
|
77
77
|
{ name: "getExecutionHistory", method: "GET", path: "/management/sessions/:sessionId/execution-history", params: { sessionId: path("sessionId"), executionId: query("number") }, summary: "Raw execution history events." },
|
|
78
|
-
{ name: "getSessionEvents", method: "GET", path: "/management/sessions/:sessionId/events", params: { sessionId: path("sessionId"), afterSeq: query("number"), limit: query("number") }, summary: "Session events after a sequence number (reconnect catch-up)." },
|
|
79
|
-
{ name: "getSessionEventsBefore", method: "GET", path: "/management/sessions/:sessionId/events-before", params: { sessionId: path("sessionId"), beforeSeq: query("number"), limit: query("number") }, summary: "Older session events for history paging." },
|
|
78
|
+
{ name: "getSessionEvents", method: "GET", path: "/management/sessions/:sessionId/events", params: { sessionId: path("sessionId"), afterSeq: query("number"), limit: query("number"), eventTypes: query("json") }, summary: "Session events after a sequence number (reconnect catch-up). Optional eventTypes (JSON string array) narrows to those event types server-side." },
|
|
79
|
+
{ name: "getSessionEventsBefore", method: "GET", path: "/management/sessions/:sessionId/events-before", params: { sessionId: path("sessionId"), beforeSeq: query("number"), limit: query("number"), eventTypes: query("json") }, summary: "Older session events for history paging. Optional eventTypes (JSON string array) narrows to those event types server-side (chat transcript paging)." },
|
|
80
80
|
{ name: "getSessionMetricSummary", method: "GET", path: "/management/sessions/:sessionId/metric-summary", params: { sessionId: path("sessionId") }, summary: "Per-session metric summary." },
|
|
81
81
|
{ name: "getSessionTokensByModel", method: "GET", path: "/management/sessions/:sessionId/tokens-by-model", params: { sessionId: path("sessionId") }, summary: "Token totals grouped by model." },
|
|
82
82
|
{ name: "getSessionTreeStats", method: "GET", path: "/management/sessions/:sessionId/tree-stats", params: { sessionId: path("sessionId") }, summary: "Stats rolled up across the spawn tree." },
|
|
@@ -84,6 +84,12 @@ export const OPERATIONS = [
|
|
|
84
84
|
{ name: "getSessionTreeSkillUsage", method: "GET", path: "/management/sessions/:sessionId/tree-skill-usage", params: { sessionId: path("sessionId"), since: query("string") }, summary: "Skill usage across the spawn tree." },
|
|
85
85
|
{ name: "getSessionFactsStats", method: "GET", path: "/management/sessions/:sessionId/facts-stats", params: { sessionId: path("sessionId") }, summary: "Facts stats for one session." },
|
|
86
86
|
{ name: "getSessionTreeFactsStats", method: "GET", path: "/management/sessions/:sessionId/tree-facts-stats", params: { sessionId: path("sessionId") }, summary: "Facts stats across the spawn tree." },
|
|
87
|
+
// Retrieval / graph observability (tuner-grade diagnostics; read-only)
|
|
88
|
+
{ name: "getSessionRetrievalUsage", method: "GET", path: "/management/sessions/:sessionId/retrieval-usage", params: { sessionId: path("sessionId"), since: query("string") }, summary: "Retrieval (facts/graph search) usage for one session." },
|
|
89
|
+
{ name: "getSessionTreeRetrievalUsage", method: "GET", path: "/management/sessions/:sessionId/tree-retrieval-usage", params: { sessionId: path("sessionId"), since: query("string") }, summary: "Retrieval usage across the spawn tree." },
|
|
90
|
+
{ name: "getSessionGraphNodeUsage", method: "GET", path: "/management/sessions/:sessionId/graph-node-usage", params: { sessionId: path("sessionId"), since: query("string"), limit: query("number"), nodeKeyLike: query("string"), kind: query("string") }, summary: "Graph node usage for one session." },
|
|
91
|
+
{ name: "getSessionGraphEdgeSearchUsage", method: "GET", path: "/management/sessions/:sessionId/graph-edge-search-usage", params: { sessionId: path("sessionId"), since: query("string"), limit: query("number") }, summary: "Graph edge-search usage for one session." },
|
|
92
|
+
{ name: "getSessionGraphSearches", method: "GET", path: "/management/sessions/:sessionId/graph-searches", params: { sessionId: path("sessionId"), limit: query("number") }, summary: "Recent graph search events for one session." },
|
|
87
93
|
{ name: "listChildOutcomes", method: "GET", path: "/management/sessions/:parentSessionId/child-outcomes", params: { parentSessionId: path("parentSessionId") }, summary: "Child outcomes recorded under a parent session." },
|
|
88
94
|
{ name: "getChildOutcome", method: "GET", path: "/management/child-outcomes/:childSessionId", params: { childSessionId: path("childSessionId") }, summary: "One child outcome." },
|
|
89
95
|
|
|
@@ -101,6 +107,7 @@ export const OPERATIONS = [
|
|
|
101
107
|
{ name: "getFleetStats", method: "GET", path: "/management/fleet/stats", params: { since: query("string"), includeDeleted: query("boolean") }, summary: "Fleet-wide stats." },
|
|
102
108
|
{ name: "getFleetSkillUsage", method: "GET", path: "/management/fleet/skill-usage", params: { since: query("string"), includeDeleted: query("boolean") }, summary: "Fleet-wide skill usage." },
|
|
103
109
|
{ name: "getFleetRetrievalUsage", method: "GET", path: "/management/fleet/retrieval-usage", params: { since: query("string"), includeDeleted: query("boolean") }, summary: "Fleet-wide retrieval usage." },
|
|
110
|
+
{ name: "getFleetGraphNodeUsage", method: "GET", path: "/management/fleet/graph-node-usage", params: { since: query("string"), includeDeleted: query("boolean"), limit: query("number"), nodeKeyLike: query("string"), kind: query("string") }, summary: "Fleet-wide graph node usage." },
|
|
104
111
|
{ name: "getUserStats", method: "GET", path: "/management/users/stats", params: { since: query("string"), includeDeleted: query("boolean") }, summary: "Per-user stats." },
|
|
105
112
|
{ name: "getSharedFactsStats", method: "GET", path: "/management/facts/shared-stats", summary: "Shared facts stats." },
|
|
106
113
|
{ name: "getFactsTombstoneStats", method: "GET", path: "/management/facts/tombstone-stats", params: { ttlSeconds: query("number") }, summary: "Soft-deleted facts awaiting reconciliation." },
|
package/dist/agent-loader.d.ts
CHANGED
|
@@ -44,6 +44,8 @@ export interface AgentConfig {
|
|
|
44
44
|
parent?: string;
|
|
45
45
|
/** Splash banner (terminal markup) shown in the TUI when the session is selected. */
|
|
46
46
|
splash?: string;
|
|
47
|
+
/** Narrow-viewport splash variant, swapped in when the main splash art is wider than the pane (mobile portal, narrow terminals). */
|
|
48
|
+
splashMobile?: string;
|
|
47
49
|
/** Initial prompt to send when the system agent is first created. */
|
|
48
50
|
initialPrompt?: string;
|
|
49
51
|
/** Source plugin namespace (e.g. "pilotswarm", "smelter"). Set by the worker during plugin loading. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agent-loader.d.ts","sourceRoot":"","sources":["../src/agent-loader.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAQH;;;GAGG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAYpD;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,eAAe,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAclF;AAID,MAAM,WAAW,WAAW;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;IACxB,wEAAwE;IACxE,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,qGAAqG;IACrG,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,qHAAqH;IACrH,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,6FAA6F;IAC7F,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,qFAAqF;IACrF,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,qEAAqE;IACrE,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,uGAAuG;IACvG,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,6EAA6E;IAC7E,eAAe,CAAC,EAAE,WAAW,GAAG,kBAAkB,GAAG,yBAAyB,CAAC;IAC/E;;;;;;;OAOG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,0EAA0E;IAC1E,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB;;;OAGG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,iFAAiF;IACjF,UAAU,CAAC,EAAE,MAAM,CAAC;CACvB;
|
|
1
|
+
{"version":3,"file":"agent-loader.d.ts","sourceRoot":"","sources":["../src/agent-loader.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAQH;;;GAGG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAYpD;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,eAAe,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAclF;AAID,MAAM,WAAW,WAAW;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;IACxB,wEAAwE;IACxE,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,qGAAqG;IACrG,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,qHAAqH;IACrH,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,6FAA6F;IAC7F,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,qFAAqF;IACrF,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,oIAAoI;IACpI,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,qEAAqE;IACrE,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,uGAAuG;IACvG,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,6EAA6E;IAC7E,eAAe,CAAC,EAAE,WAAW,GAAG,kBAAkB,GAAG,yBAAyB,CAAC;IAC/E;;;;;;;OAOG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,0EAA0E;IAC1E,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB;;;OAGG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,iFAAiF;IACjF,UAAU,CAAC,EAAE,MAAM,CAAC;CACvB;AAyHD;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG,WAAW,EAAE,CAiE/D"}
|
package/dist/agent-loader.js
CHANGED
|
@@ -83,6 +83,8 @@ function parseAgentFrontmatter(content) {
|
|
|
83
83
|
const val = multilineValue.join("\n").trimEnd();
|
|
84
84
|
if (currentKey === "splash")
|
|
85
85
|
meta.splash = val;
|
|
86
|
+
else if (currentKey === "splashMobile")
|
|
87
|
+
meta.splashMobile = val;
|
|
86
88
|
else if (currentKey === "initialPrompt") {
|
|
87
89
|
// For > (folded) scalars, collapse newlines to spaces
|
|
88
90
|
meta.initialPrompt = currentBlockStyle === ">" ? val.replace(/\n/g, " ").trim() : val;
|
|
@@ -156,7 +158,7 @@ function parseAgentFrontmatter(content) {
|
|
|
156
158
|
// Will be followed by list items
|
|
157
159
|
meta.tools = [];
|
|
158
160
|
}
|
|
159
|
-
else if ((key === "splash" || key === "initialPrompt") && (value === "|" || value === ">")) {
|
|
161
|
+
else if ((key === "splash" || key === "splashMobile" || key === "initialPrompt") && (value === "|" || value === ">")) {
|
|
160
162
|
// YAML block scalar (| literal, > folded)
|
|
161
163
|
currentBlockStyle = value;
|
|
162
164
|
multilineValue = [];
|
|
@@ -164,6 +166,9 @@ function parseAgentFrontmatter(content) {
|
|
|
164
166
|
else if (key === "splash") {
|
|
165
167
|
meta.splash = value;
|
|
166
168
|
}
|
|
169
|
+
else if (key === "splashMobile") {
|
|
170
|
+
meta.splashMobile = value;
|
|
171
|
+
}
|
|
167
172
|
else if (key === "initialPrompt") {
|
|
168
173
|
meta.initialPrompt = value;
|
|
169
174
|
}
|
|
@@ -216,6 +221,7 @@ export function loadAgentFiles(agentsDir) {
|
|
|
216
221
|
title: meta.title,
|
|
217
222
|
parent: meta.parent,
|
|
218
223
|
splash: meta.splash,
|
|
224
|
+
splashMobile: meta.splashMobile,
|
|
219
225
|
initialPrompt: meta.initialPrompt,
|
|
220
226
|
crawler,
|
|
221
227
|
harvester: crawler,
|
package/dist/agent-loader.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agent-loader.js","sourceRoot":"","sources":["../src/agent-loader.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,MAAM,MAAM,aAAa,CAAC;AAEjC,mEAAmE;AAEnE;;;GAGG;AACH,MAAM,UAAU,eAAe,CAAC,IAAY;IACxC,MAAM,IAAI,GAAG,MAAM,CAAC,UAAU,CAAC,QAAQ,CAAC;SACnC,MAAM,CAAC,0BAA0B,CAAC;SAClC,MAAM,CAAC,IAAI,CAAC;SACZ,MAAM,CAAC,KAAK,CAAC,CAAC;IACnB,OAAO;QACH,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC;QAChB,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC;QACjB,IAAI,CAAC,KAAK,CAAC,EAAE,EAAE,EAAE,CAAC;QAClB,IAAI,CAAC,KAAK,CAAC,EAAE,EAAE,EAAE,CAAC;QAClB,IAAI,CAAC,KAAK,CAAC,EAAE,EAAE,EAAE,CAAC;KACrB,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAChB,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,oBAAoB,CAAC,eAAuB,EAAE,IAAY;IACtE,MAAM,IAAI,GAAG,MAAM,CAAC,UAAU,CAAC,QAAQ,CAAC;SACnC,MAAM,CAAC,gCAAgC,CAAC;SACxC,MAAM,CAAC,eAAe,CAAC;SACvB,MAAM,CAAC,GAAG,CAAC;SACX,MAAM,CAAC,IAAI,CAAC;SACZ,MAAM,CAAC,KAAK,CAAC,CAAC;IACnB,OAAO;QACH,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC;QAChB,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC;QACjB,IAAI,CAAC,KAAK,CAAC,EAAE,EAAE,EAAE,CAAC;QAClB,IAAI,CAAC,KAAK,CAAC,EAAE,EAAE,EAAE,CAAC;QAClB,IAAI,CAAC,KAAK,CAAC,EAAE,EAAE,EAAE,CAAC;KACrB,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAChB,CAAC;
|
|
1
|
+
{"version":3,"file":"agent-loader.js","sourceRoot":"","sources":["../src/agent-loader.ts"],"names":[],"mappings":"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"}
|
package/dist/blob-store.d.ts
CHANGED
|
@@ -27,6 +27,7 @@
|
|
|
27
27
|
* unmigrated stamps stay on the legacy path.
|
|
28
28
|
*/
|
|
29
29
|
import { ContainerClient, StorageSharedKeyCredential } from "@azure/storage-blob";
|
|
30
|
+
import { type SnapshotCommitInput, type SnapshotCommitResult, type SnapshotHydrateResult, type SnapshotProbe, type VersionedSnapshotStore } from "./snapshot-protocol.js";
|
|
30
31
|
import { type ArtifactDownloadResult, type ArtifactMetadata, type SessionStateStore, type ArtifactStore, type ArtifactUploadOptions } from "./session-store.js";
|
|
31
32
|
/**
|
|
32
33
|
* Configuration for constructing a {@link SessionBlobStore} against an
|
|
@@ -70,7 +71,7 @@ export interface SessionBlobStoreClientConfig {
|
|
|
70
71
|
*
|
|
71
72
|
* @internal
|
|
72
73
|
*/
|
|
73
|
-
export declare class SessionBlobStore implements SessionStateStore, ArtifactStore {
|
|
74
|
+
export declare class SessionBlobStore implements SessionStateStore, ArtifactStore, VersionedSnapshotStore {
|
|
74
75
|
private containerClient;
|
|
75
76
|
private containerName;
|
|
76
77
|
private credential;
|
|
@@ -97,6 +98,14 @@ export declare class SessionBlobStore implements SessionStateStore, ArtifactStor
|
|
|
97
98
|
exists(sessionId: string): Promise<boolean>;
|
|
98
99
|
/** Delete a dehydrated session from blob storage. */
|
|
99
100
|
delete(sessionId: string): Promise<void>;
|
|
101
|
+
private static readonly COMMIT_MAX_ATTEMPTS;
|
|
102
|
+
private static readonly SINGLE_SHOT_MAX_BYTES;
|
|
103
|
+
private snapshotBlobName;
|
|
104
|
+
private probeFromMetadata;
|
|
105
|
+
private headSnapshot;
|
|
106
|
+
probeSnapshot(sessionId: string): Promise<SnapshotProbe>;
|
|
107
|
+
commitSnapshot(sessionId: string, input: SnapshotCommitInput): Promise<SnapshotCommitResult>;
|
|
108
|
+
hydrateSnapshot(sessionId: string): Promise<SnapshotHydrateResult>;
|
|
100
109
|
private artifactBlobPath;
|
|
101
110
|
/**
|
|
102
111
|
* Upload an artifact file (e.g. .md) to blob storage.
|
package/dist/blob-store.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"blob-store.d.ts","sourceRoot":"","sources":["../src/blob-store.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,OAAO,EAEH,eAAe,EACf,0BAA0B,EAI7B,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"blob-store.d.ts","sourceRoot":"","sources":["../src/blob-store.ts"],"names":[],"mappings":"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"}
|
package/dist/blob-store.js
CHANGED
|
@@ -28,10 +28,13 @@
|
|
|
28
28
|
*/
|
|
29
29
|
import { BlobServiceClient, StorageSharedKeyCredential, generateBlobSASQueryParameters, BlobSASPermissions, SASProtocol, } from "@azure/storage-blob";
|
|
30
30
|
import { DefaultAzureCredential } from "@azure/identity";
|
|
31
|
+
import crypto from "node:crypto";
|
|
31
32
|
import fs from "node:fs";
|
|
32
33
|
import os from "node:os";
|
|
33
34
|
import path from "node:path";
|
|
34
|
-
import {
|
|
35
|
+
import { faultPoint } from "./fault-injection.js";
|
|
36
|
+
import { SnapshotConflictError, } from "./snapshot-protocol.js";
|
|
37
|
+
import { DEFAULT_SESSION_STATE_DIR, DEFAULT_SNAPSHOT_CODEC, archiveSessionDir, buildMetadata, extractSessionArchive, isBinaryArtifactContentType, normalizeArtifactContentType, resolveArtifactUpload, resolveSnapshotCodec, waitForSessionSnapshot, } from "./session-store.js";
|
|
35
38
|
function formatBlobLogValue(value) {
|
|
36
39
|
if (value == null)
|
|
37
40
|
return "";
|
|
@@ -130,6 +133,27 @@ export class SessionBlobStore {
|
|
|
130
133
|
dir: sessionDir,
|
|
131
134
|
reason: meta?.reason,
|
|
132
135
|
});
|
|
136
|
+
// Versioned-snapshot fence: the committed chain (lifecycle protocol)
|
|
137
|
+
// already holds this session's durable state, and an unconditional
|
|
138
|
+
// legacy Put would replace both content and the CAS metadata
|
|
139
|
+
// (psver/psturnkey/pssha). Degrade to release: free local files.
|
|
140
|
+
try {
|
|
141
|
+
const head = await this.headSnapshot(sessionId);
|
|
142
|
+
if (head.exists && !head.legacy) {
|
|
143
|
+
logBlobStore("warn", sessionId, "dehydrate skipped upload: versioned snapshot exists; releasing local files only", {
|
|
144
|
+
container: this.containerName,
|
|
145
|
+
version: head.version,
|
|
146
|
+
});
|
|
147
|
+
fs.rmSync(sessionDir, { recursive: true, force: true });
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
catch (probeErr) {
|
|
152
|
+
logBlobStore("warn", sessionId, "dehydrate version probe failed; proceeding with legacy upload", {
|
|
153
|
+
container: this.containerName,
|
|
154
|
+
error: errorMessage(probeErr),
|
|
155
|
+
});
|
|
156
|
+
}
|
|
133
157
|
const snapshot = await waitForSessionSnapshot(this.sessionStateDir, sessionId);
|
|
134
158
|
if (!snapshot.ready) {
|
|
135
159
|
logBlobStore("warn", sessionId, "dehydrate snapshot not ready", {
|
|
@@ -139,20 +163,24 @@ export class SessionBlobStore {
|
|
|
139
163
|
throw new Error(`Session state directory not ready during dehydrate: ${sessionId} (${sessionDir}). ` +
|
|
140
164
|
`Missing: ${snapshot.missing.join(", ") || "unknown"}`);
|
|
141
165
|
}
|
|
142
|
-
const
|
|
166
|
+
const codec = DEFAULT_SNAPSHOT_CODEC;
|
|
167
|
+
const tarPath = path.join(os.tmpdir(), `${sessionId}.tar`);
|
|
143
168
|
try {
|
|
144
|
-
archiveSessionDir(this.sessionStateDir, sessionId, tarPath);
|
|
169
|
+
const { rawSizeBytes } = await archiveSessionDir(this.sessionStateDir, sessionId, tarPath, codec);
|
|
145
170
|
const tarSizeBytes = fs.existsSync(tarPath) ? fs.statSync(tarPath).size : undefined;
|
|
146
|
-
// Upload tar
|
|
171
|
+
// Upload tar. The `pscodec` metadata on the tar blob is what the
|
|
172
|
+
// read path decodes by — brotli has no magic bytes, so an
|
|
173
|
+
// unversioned legacy blob without it is assumed gzip.
|
|
147
174
|
const tarBlob = this.containerClient.getBlockBlobClient(`${sessionId}.tar.gz`);
|
|
148
175
|
logBlobStore("info", sessionId, "dehydrate upload tar", {
|
|
149
176
|
container: this.containerName,
|
|
150
177
|
blob: `${sessionId}.tar.gz`,
|
|
151
178
|
tarSizeBytes,
|
|
179
|
+
codec,
|
|
152
180
|
});
|
|
153
|
-
await tarBlob.uploadFile(tarPath);
|
|
181
|
+
await tarBlob.uploadFile(tarPath, { metadata: { pscodec: codec, psraw: String(rawSizeBytes) } });
|
|
154
182
|
// Upload metadata
|
|
155
|
-
const metadata = buildMetadata(tarPath, sessionId, meta);
|
|
183
|
+
const metadata = buildMetadata(tarPath, sessionId, { ...meta, codec, rawSizeBytes });
|
|
156
184
|
this.snapshotSizeBySession.set(sessionId, metadata.sizeBytes);
|
|
157
185
|
const metaBlob = this.containerClient.getBlockBlobClient(`${sessionId}.meta.json`);
|
|
158
186
|
const metaJson = JSON.stringify(metadata);
|
|
@@ -199,14 +227,15 @@ export class SessionBlobStore {
|
|
|
199
227
|
fs.rmSync(sessionDir, { recursive: true, force: true });
|
|
200
228
|
}
|
|
201
229
|
const tarBlob = this.containerClient.getBlockBlobClient(`${sessionId}.tar.gz`);
|
|
202
|
-
const tarPath = path.join(os.tmpdir(), `${sessionId}.tar
|
|
230
|
+
const tarPath = path.join(os.tmpdir(), `${sessionId}.tar`);
|
|
203
231
|
try {
|
|
204
232
|
logBlobStore("info", sessionId, "hydrate download tar", {
|
|
205
233
|
container: this.containerName,
|
|
206
234
|
blob: `${sessionId}.tar.gz`,
|
|
207
235
|
});
|
|
208
|
-
await tarBlob.downloadToFile(tarPath);
|
|
209
|
-
|
|
236
|
+
const response = await tarBlob.downloadToFile(tarPath);
|
|
237
|
+
const codec = resolveSnapshotCodec(response.metadata?.pscodec);
|
|
238
|
+
await extractSessionArchive(this.sessionStateDir, tarPath, codec);
|
|
210
239
|
logBlobStore("info", sessionId, "hydrate complete", {
|
|
211
240
|
container: this.containerName,
|
|
212
241
|
restoredDir: sessionDir,
|
|
@@ -240,18 +269,37 @@ export class SessionBlobStore {
|
|
|
240
269
|
});
|
|
241
270
|
return;
|
|
242
271
|
}
|
|
243
|
-
|
|
272
|
+
// Versioned-snapshot fence (see dehydrate): never clobber the
|
|
273
|
+
// CAS-protected chain with an unversioned legacy write.
|
|
274
|
+
try {
|
|
275
|
+
const head = await this.headSnapshot(sessionId);
|
|
276
|
+
if (head.exists && !head.legacy) {
|
|
277
|
+
logBlobStore("warn", sessionId, "checkpoint skipped: versioned snapshot exists", {
|
|
278
|
+
container: this.containerName,
|
|
279
|
+
version: head.version,
|
|
280
|
+
});
|
|
281
|
+
return;
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
catch (probeErr) {
|
|
285
|
+
logBlobStore("warn", sessionId, "checkpoint version probe failed; proceeding with legacy upload", {
|
|
286
|
+
container: this.containerName,
|
|
287
|
+
error: errorMessage(probeErr),
|
|
288
|
+
});
|
|
289
|
+
}
|
|
290
|
+
const codec = DEFAULT_SNAPSHOT_CODEC;
|
|
291
|
+
const tarPath = path.join(os.tmpdir(), `${sessionId}.tar`);
|
|
244
292
|
try {
|
|
245
293
|
logBlobStore("info", sessionId, "checkpoint start", {
|
|
246
294
|
container: this.containerName,
|
|
247
295
|
dir: sessionDir,
|
|
248
296
|
});
|
|
249
|
-
archiveSessionDir(this.sessionStateDir, sessionId, tarPath);
|
|
297
|
+
const { rawSizeBytes } = await archiveSessionDir(this.sessionStateDir, sessionId, tarPath, codec);
|
|
250
298
|
const tarSizeBytes = fs.existsSync(tarPath) ? fs.statSync(tarPath).size : undefined;
|
|
251
299
|
const tarBlob = this.containerClient.getBlockBlobClient(`${sessionId}.tar.gz`);
|
|
252
|
-
await tarBlob.uploadFile(tarPath);
|
|
300
|
+
await tarBlob.uploadFile(tarPath, { metadata: { pscodec: codec, psraw: String(rawSizeBytes) } });
|
|
253
301
|
// Update metadata to reflect checkpoint (not full dehydration)
|
|
254
|
-
const metadata = buildMetadata(tarPath, sessionId, { reason: "checkpoint" });
|
|
302
|
+
const metadata = buildMetadata(tarPath, sessionId, { reason: "checkpoint", codec, rawSizeBytes });
|
|
255
303
|
this.snapshotSizeBySession.set(sessionId, metadata.sizeBytes);
|
|
256
304
|
const metaBlob = this.containerClient.getBlockBlobClient(`${sessionId}.meta.json`);
|
|
257
305
|
const metaJson = JSON.stringify(metadata);
|
|
@@ -347,6 +395,220 @@ export class SessionBlobStore {
|
|
|
347
395
|
throw error;
|
|
348
396
|
}
|
|
349
397
|
}
|
|
398
|
+
// ─── Versioned CAS contract (session-lifecycle-protocol §3.1) ───
|
|
399
|
+
//
|
|
400
|
+
// The version is an explicit counter in the tar blob's metadata
|
|
401
|
+
// (`psver`, plus `psturnkey` + `pssha`), written atomically with the
|
|
402
|
+
// content by single-shot Put Blob. The ETag is never the version — it is
|
|
403
|
+
// the atomicity token binding each HEAD to the conditional PUT that
|
|
404
|
+
// follows it (`If-Match`). A blob without `psver` is a legacy snapshot
|
|
405
|
+
// (probe reports version 0 / legacy). Two footguns, per the store
|
|
406
|
+
// proposal: never staged Put Block uploads (the condition only applies
|
|
407
|
+
// at commit and concurrent stagers interleave), and never Set Blob
|
|
408
|
+
// Metadata for version bumps (it would decouple counter from content).
|
|
409
|
+
static COMMIT_MAX_ATTEMPTS = 5;
|
|
410
|
+
static SINGLE_SHOT_MAX_BYTES = 256 * 1024 * 1024;
|
|
411
|
+
snapshotBlobName(sessionId) {
|
|
412
|
+
return `${sessionId}.tar.gz`;
|
|
413
|
+
}
|
|
414
|
+
probeFromMetadata(metadata, etag) {
|
|
415
|
+
const codec = resolveSnapshotCodec(metadata?.pscodec);
|
|
416
|
+
const raw = Number(metadata?.psraw);
|
|
417
|
+
const version = Number(metadata?.psver);
|
|
418
|
+
if (!Number.isFinite(version) || version < 1) {
|
|
419
|
+
return { exists: true, version: 0, legacy: true, codec, ...(etag ? { etag } : {}) };
|
|
420
|
+
}
|
|
421
|
+
return {
|
|
422
|
+
exists: true,
|
|
423
|
+
version,
|
|
424
|
+
...(metadata?.psturnkey ? { turnKey: metadata.psturnkey } : {}),
|
|
425
|
+
...(metadata?.pssha ? { contentHash: metadata.pssha } : {}),
|
|
426
|
+
...(Number.isFinite(raw) ? { rawSizeBytes: raw } : {}),
|
|
427
|
+
codec,
|
|
428
|
+
...(etag ? { etag } : {}),
|
|
429
|
+
};
|
|
430
|
+
}
|
|
431
|
+
async headSnapshot(sessionId) {
|
|
432
|
+
const blob = this.containerClient.getBlockBlobClient(this.snapshotBlobName(sessionId));
|
|
433
|
+
try {
|
|
434
|
+
const props = await blob.getProperties();
|
|
435
|
+
return this.probeFromMetadata(props.metadata, props.etag);
|
|
436
|
+
}
|
|
437
|
+
catch (error) {
|
|
438
|
+
if (error?.statusCode === 404)
|
|
439
|
+
return { exists: false, version: 0 };
|
|
440
|
+
throw error;
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
async probeSnapshot(sessionId) {
|
|
444
|
+
const { etag: _etag, ...probe } = await this.headSnapshot(sessionId);
|
|
445
|
+
return probe;
|
|
446
|
+
}
|
|
447
|
+
async commitSnapshot(sessionId, input) {
|
|
448
|
+
const sessionDir = path.join(this.sessionStateDir, sessionId);
|
|
449
|
+
const snapshot = await waitForSessionSnapshot(this.sessionStateDir, sessionId);
|
|
450
|
+
if (!snapshot.ready) {
|
|
451
|
+
throw new Error(`Session state directory not ready during commit: ${sessionId} (${sessionDir}). ` +
|
|
452
|
+
`Missing: ${snapshot.missing.join(", ") || "unknown"}`);
|
|
453
|
+
}
|
|
454
|
+
const codec = DEFAULT_SNAPSHOT_CODEC;
|
|
455
|
+
const tarPath = path.join(os.tmpdir(), `ps-commit-${sessionId}-${process.pid}-${Date.now()}.tar`);
|
|
456
|
+
try {
|
|
457
|
+
const { rawSizeBytes } = await archiveSessionDir(this.sessionStateDir, sessionId, tarPath, codec);
|
|
458
|
+
const body = fs.readFileSync(tarPath);
|
|
459
|
+
if (body.length > SessionBlobStore.SINGLE_SHOT_MAX_BYTES) {
|
|
460
|
+
throw new Error(`Session snapshot for ${sessionId} is ${body.length} bytes — over the ` +
|
|
461
|
+
`${SessionBlobStore.SINGLE_SHOT_MAX_BYTES} single-shot commit cap. ` +
|
|
462
|
+
`Audit the session workspace for files that belong in tar excludes.`);
|
|
463
|
+
}
|
|
464
|
+
const contentHash = crypto.createHash("sha256").update(body).digest("hex");
|
|
465
|
+
const blob = this.containerClient.getBlockBlobClient(this.snapshotBlobName(sessionId));
|
|
466
|
+
for (let attempt = 1; attempt <= SessionBlobStore.COMMIT_MAX_ATTEMPTS; attempt++) {
|
|
467
|
+
const head = await this.headSnapshot(sessionId);
|
|
468
|
+
if (head.exists && head.version === input.baseVersion + 1 && head.turnKey === input.turnKey) {
|
|
469
|
+
logBlobStore("info", sessionId, "commit already landed (idempotent retry)", {
|
|
470
|
+
version: head.version,
|
|
471
|
+
turnKey: input.turnKey,
|
|
472
|
+
});
|
|
473
|
+
return {
|
|
474
|
+
version: head.version,
|
|
475
|
+
contentHash: head.contentHash ?? "",
|
|
476
|
+
...(head.rawSizeBytes != null ? { rawSizeBytes: head.rawSizeBytes } : {}),
|
|
477
|
+
alreadyCommitted: true,
|
|
478
|
+
};
|
|
479
|
+
}
|
|
480
|
+
if (head.exists && head.version !== input.baseVersion) {
|
|
481
|
+
throw new SnapshotConflictError(sessionId, input.baseVersion, head.version, head.turnKey);
|
|
482
|
+
}
|
|
483
|
+
// head.exists && version === baseVersion (legacy counts as 0),
|
|
484
|
+
// or !exists (any baseVersion commits as a fresh chain — the
|
|
485
|
+
// store lost data and the worker's copy is the only truth).
|
|
486
|
+
const version = head.exists ? head.version + 1 : input.baseVersion + 1;
|
|
487
|
+
const conditions = head.exists && head.etag
|
|
488
|
+
? { ifMatch: head.etag }
|
|
489
|
+
: { ifNoneMatch: "*" };
|
|
490
|
+
faultPoint("store.commit.before-write");
|
|
491
|
+
try {
|
|
492
|
+
await blob.upload(body, body.length, {
|
|
493
|
+
conditions,
|
|
494
|
+
metadata: {
|
|
495
|
+
psver: String(version),
|
|
496
|
+
psturnkey: input.turnKey,
|
|
497
|
+
pssha: contentHash,
|
|
498
|
+
pscodec: codec,
|
|
499
|
+
psraw: String(rawSizeBytes),
|
|
500
|
+
},
|
|
501
|
+
});
|
|
502
|
+
faultPoint("store.commit.after-write");
|
|
503
|
+
// Legacy compat: keep <sessionId>.meta.json fresh so
|
|
504
|
+
// getSnapshotSizeBytes and older tooling keep working.
|
|
505
|
+
// Unconditional + best-effort — it is informational only.
|
|
506
|
+
try {
|
|
507
|
+
const metadata = {
|
|
508
|
+
...buildMetadata(tarPath, sessionId, { reason: "turn-commit" }),
|
|
509
|
+
version,
|
|
510
|
+
turnKey: input.turnKey,
|
|
511
|
+
contentHash,
|
|
512
|
+
codec,
|
|
513
|
+
rawSizeBytes,
|
|
514
|
+
};
|
|
515
|
+
this.snapshotSizeBySession.set(sessionId, metadata.sizeBytes);
|
|
516
|
+
const metaBlob = this.containerClient.getBlockBlobClient(`${sessionId}.meta.json`);
|
|
517
|
+
const metaJson = JSON.stringify(metadata);
|
|
518
|
+
await metaBlob.upload(metaJson, metaJson.length);
|
|
519
|
+
}
|
|
520
|
+
catch (metaErr) {
|
|
521
|
+
logBlobStore("warn", sessionId, "commit meta.json refresh failed (non-fatal)", {
|
|
522
|
+
error: errorMessage(metaErr),
|
|
523
|
+
});
|
|
524
|
+
}
|
|
525
|
+
logBlobStore("info", sessionId, "commit complete", {
|
|
526
|
+
version,
|
|
527
|
+
turnKey: input.turnKey,
|
|
528
|
+
tarSizeBytes: body.length,
|
|
529
|
+
rawSizeBytes,
|
|
530
|
+
codec,
|
|
531
|
+
attempt,
|
|
532
|
+
});
|
|
533
|
+
return { version, contentHash, sizeBytes: body.length, rawSizeBytes, alreadyCommitted: false };
|
|
534
|
+
}
|
|
535
|
+
catch (error) {
|
|
536
|
+
// 412 Precondition Failed (If-Match lost the race) or
|
|
537
|
+
// 409 BlobAlreadyExists (If-None-Match create race):
|
|
538
|
+
// re-HEAD and re-evaluate — the winner may have been our
|
|
539
|
+
// own prior attempt (idempotent success) or a foreign
|
|
540
|
+
// writer (conflict), both handled at the top of the loop.
|
|
541
|
+
const status = error?.statusCode;
|
|
542
|
+
if (status === 412 || status === 409) {
|
|
543
|
+
logBlobStore("warn", sessionId, "commit CAS race, re-evaluating", {
|
|
544
|
+
attempt,
|
|
545
|
+
status,
|
|
546
|
+
});
|
|
547
|
+
continue;
|
|
548
|
+
}
|
|
549
|
+
throw error;
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
throw new Error(`Snapshot commit for ${sessionId} exhausted CAS retries`);
|
|
553
|
+
}
|
|
554
|
+
finally {
|
|
555
|
+
try {
|
|
556
|
+
fs.unlinkSync(tarPath);
|
|
557
|
+
}
|
|
558
|
+
catch { }
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
async hydrateSnapshot(sessionId) {
|
|
562
|
+
const sessionDir = path.join(this.sessionStateDir, sessionId);
|
|
563
|
+
const blob = this.containerClient.getBlockBlobClient(this.snapshotBlobName(sessionId));
|
|
564
|
+
const tarPath = path.join(os.tmpdir(), `ps-hydrate-${sessionId}-${process.pid}-${Date.now()}.tar`);
|
|
565
|
+
// One download response carries body + metadata consistently.
|
|
566
|
+
let metadata;
|
|
567
|
+
let tarSizeBytes;
|
|
568
|
+
try {
|
|
569
|
+
const response = await blob.downloadToFile(tarPath, 0);
|
|
570
|
+
metadata = response.metadata;
|
|
571
|
+
try {
|
|
572
|
+
tarSizeBytes = fs.statSync(tarPath).size;
|
|
573
|
+
}
|
|
574
|
+
catch { }
|
|
575
|
+
const codec = resolveSnapshotCodec(metadata?.pscodec);
|
|
576
|
+
fs.mkdirSync(this.sessionStateDir, { recursive: true });
|
|
577
|
+
const tempRoot = fs.mkdtempSync(path.join(this.sessionStateDir, `.ps-hydrate-${sessionId}-`));
|
|
578
|
+
try {
|
|
579
|
+
await extractSessionArchive(tempRoot, tarPath, codec);
|
|
580
|
+
const extracted = path.join(tempRoot, sessionId);
|
|
581
|
+
if (!fs.existsSync(extracted)) {
|
|
582
|
+
throw new Error(`Snapshot archive for ${sessionId} did not contain the session directory`);
|
|
583
|
+
}
|
|
584
|
+
faultPoint("store.hydrate.before-swap");
|
|
585
|
+
fs.rmSync(sessionDir, { recursive: true, force: true });
|
|
586
|
+
fs.renameSync(extracted, sessionDir);
|
|
587
|
+
}
|
|
588
|
+
finally {
|
|
589
|
+
fs.rmSync(tempRoot, { recursive: true, force: true });
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
finally {
|
|
593
|
+
try {
|
|
594
|
+
fs.unlinkSync(tarPath);
|
|
595
|
+
}
|
|
596
|
+
catch { }
|
|
597
|
+
}
|
|
598
|
+
const probe = this.probeFromMetadata(metadata, undefined);
|
|
599
|
+
logBlobStore("info", sessionId, "versioned hydrate complete", {
|
|
600
|
+
version: probe.version,
|
|
601
|
+
legacy: probe.legacy,
|
|
602
|
+
});
|
|
603
|
+
return {
|
|
604
|
+
version: probe.version,
|
|
605
|
+
...(probe.turnKey ? { turnKey: probe.turnKey } : {}),
|
|
606
|
+
...(probe.contentHash ? { contentHash: probe.contentHash } : {}),
|
|
607
|
+
...(tarSizeBytes != null ? { sizeBytes: tarSizeBytes } : {}),
|
|
608
|
+
...(probe.rawSizeBytes != null ? { rawSizeBytes: probe.rawSizeBytes } : {}),
|
|
609
|
+
...(probe.legacy ? { legacy: true } : {}),
|
|
610
|
+
};
|
|
611
|
+
}
|
|
350
612
|
// ─── Artifact Storage ────────────────────────────────────
|
|
351
613
|
artifactBlobPath(sessionId, filename) {
|
|
352
614
|
// Sanitize filename — strip path separators
|