@prismshadow/penguin-server 0.1.2 → 0.1.5

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.
@@ -116,6 +116,13 @@ interface ProjectCreateRequest {
116
116
  interface ProjectCreateResponse {
117
117
  project: ProjectSummary;
118
118
  }
119
+ interface ProjectUpdateRequest {
120
+ /** New display name. The projectId itself is immutable — only this label can change. */
121
+ name: string;
122
+ }
123
+ interface ProjectUpdateResponse {
124
+ project: ProjectSummary;
125
+ }
119
126
  interface MemberInfo {
120
127
  userId: string;
121
128
  role: ProjectRole;
@@ -186,6 +193,13 @@ interface ModelsResponse {
186
193
  defaultModel?: ModelRefDto;
187
194
  /** Vision model used as a proxy reader for read_image (describes images when the session model has vision=false). */
188
195
  visionModel?: ModelRefDto;
196
+ /**
197
+ * When the Project's model/credential config last changed (ISO; the config file's mtime,
198
+ * so it survives restarts). The web's auth-dead gate compares it against the last auth
199
+ * abort: an abort OLDER than the last credential update no longer disables the composer
200
+ * (the key was fixed since). Absent when the Project has no config file yet.
201
+ */
202
+ updatedAt?: string;
189
203
  models: ModelInfo[];
190
204
  }
191
205
  /** PUT full-table replace semantics: models not present are deleted; omitting apiKey = keep existing value. Key = (provider, modelId). */
@@ -467,12 +481,44 @@ interface SessionPatchRequest {
467
481
  /** Manual rename; non-empty string, overrides the auto-generated title. */
468
482
  title?: string;
469
483
  }
484
+ /**
485
+ * Live in-progress tail of a running Session, carried by `MessagesResponse.live`.
486
+ *
487
+ * Contract (see runtime/live-tail.ts and the GET /messages route): the server captures
488
+ * `cursor` and `fragments` atomically — in one synchronous tick, before starting the
489
+ * trace read — while the Session is running/compacting.
490
+ * - `cursor`: the Session channel's most recently assigned SSE event id
491
+ * (`<epoch>-<seq>`); every event published up to and including this id is already
492
+ * reflected in `fragments`.
493
+ * - `fragments`: one synthetic `partial_* start` OmniMessage per open streaming
494
+ * fragment, whose payload carries the full accumulated content so far (text/thinking
495
+ * prefix, tool-call name + accumulated arguments, tool-output prefix + images), with
496
+ * the original `origin` chain preserved.
497
+ *
498
+ * Client usage (the bundled Web App's connect-first flow): after applying `messages`,
499
+ * when the cursor's epoch matches the epoch of the SSE events seen on the current
500
+ * connection, drop every buffered **partial** event with seq <= cursor (its content is
501
+ * already inside `fragments`), feed `fragments` through the normal reducer path, then
502
+ * replay the rest of the buffer. Buffered **complete** messages are never dropped by the
503
+ * cursor — the regular overlap dedup decides for them — so nothing is lost even when a
504
+ * complete message's trace append is still in flight at read time.
505
+ */
506
+ interface MessagesLiveTail {
507
+ cursor: string;
508
+ fragments: OmniMessage[];
509
+ }
470
510
  /** Message history: the full messages and events from concatenating all of this Session's Trace files in order (excludes partial_*). */
471
511
  interface MessagesResponse {
472
512
  messages: OmniMessage[];
513
+ /**
514
+ * Present only while the Session is running/compacting: the in-progress stream tail
515
+ * (open streaming fragments + the channel cursor they cover), so a client joining
516
+ * mid-stream can render the currently streaming message. Omitted when idle.
517
+ */
518
+ live?: MessagesLiveTail;
473
519
  }
474
520
  /**
475
- * A single Prompt's input parts: text or image (data: / http(s) URL).
521
+ * A single Prompt's input parts: text, image (data: / http(s) URL), or an uploaded file.
476
522
  * Docs: /docs/server-api § "Session-Level Endpoints".
477
523
  */
478
524
  type TaskInputPart = {
@@ -481,6 +527,19 @@ type TaskInputPart = {
481
527
  } | {
482
528
  type: "image_url";
483
529
  imageUrl: string;
530
+ }
531
+ /**
532
+ * File attachment (the composer's "+" menu): `dataUrl` is a base64 `data:` URL of the
533
+ * file's bytes, capped at 10MB each (413 `file_too_large` beyond that; the request as a
534
+ * whole still has to fit the global 20MB body limit). The server writes it into the
535
+ * Session scratchpad under a sanitized name and appends an `[attached file: <path>]` line
536
+ * to the message text — the bytes never enter the conversation, the model opens the file
537
+ * by path. `fileName` is the original name (no path separators, no `..`).
538
+ */
539
+ | {
540
+ type: "file";
541
+ fileName: string;
542
+ dataUrl: string;
484
543
  };
485
544
  interface TaskCreateRequest {
486
545
  input: TaskInputPart[];
@@ -497,6 +556,29 @@ interface TaskCreateRequest {
497
556
  * (in queue order, one at a time). The response then carries `queued: true`.
498
557
  */
499
558
  queueIfBusy?: boolean;
559
+ /**
560
+ * Present = goal mode: the input's text becomes the objective (leading `[use_skills]`
561
+ * blocks and the like are stripped from the recorded objective; the round-1 message keeps
562
+ * them) and the server loops the Session until the goal reaches a terminal state.
563
+ * `budget` is the token budget (uncached input + output); omitted or -1 = unlimited.
564
+ */
565
+ goal?: {
566
+ budget?: number;
567
+ };
568
+ }
569
+ /** Goal-mode run state (from goal_state; the chat page's banner restores from the latest row). */
570
+ interface GoalStateView {
571
+ objective: string;
572
+ status: "active" | "complete" | "blocked" | "budget_limited" | "aborted";
573
+ /** Token budget; -1 = unlimited. */
574
+ budget: number;
575
+ used: number;
576
+ rounds: number;
577
+ updatedAt: string;
578
+ }
579
+ interface GoalResponse {
580
+ /** The Session's most recent goal run; null if it never ran one. */
581
+ goal: GoalStateView | null;
500
582
  }
501
583
  interface TaskCreateResponse {
502
584
  /** Current actual session_id: a Trace-less invalid Session self-heals and returns a new id; the frontend updates its route accordingly. */
@@ -511,12 +593,29 @@ interface TaskCreateResponse {
511
593
  * task POST).
512
594
  */
513
595
  interface SteerRequest {
514
- /** Non-empty message text (trimmed server-side). */
596
+ /** Message text (trimmed server-side); may be empty when `images` carries the message. */
515
597
  text: string;
598
+ /**
599
+ * Images sent with the steering message (`data:` or http(s) URLs, same rule as
600
+ * `TaskInputPart.image_url`): delivered as user image messages right behind the
601
+ * `[user_steering]` text. A model without vision receives them as scratchpad path lines
602
+ * instead, exactly as it would a Prompt's images. At least one of `text` / `images` must
603
+ * be non-empty.
604
+ */
605
+ images?: string[];
516
606
  }
517
607
  interface ApprovalDecisionRequest {
518
608
  decision: "allow" | "deny";
519
609
  }
610
+ /**
611
+ * POST /api/sessions/:sessionId/retry-now — skip the in-progress reconnect backoff and
612
+ * fire the next retry immediately (the "retry now" button on the reconnect countdown).
613
+ * `skipped: false` is the benign "no reconnect wait in progress" case (idle session, or
614
+ * the wait elapsed in a timing race), not an error.
615
+ */
616
+ interface RetryNowResponse {
617
+ skipped: boolean;
618
+ }
520
619
  /** Docs: /docs/server-api § "Streaming (SSE)". */
521
620
  type ServerEvent =
522
621
  /**
@@ -544,6 +643,16 @@ type ServerEvent =
544
643
  | {
545
644
  type: "resync_required";
546
645
  }
646
+ /**
647
+ * The Project's model credentials changed (PUT /models): cached runtimes have been
648
+ * invalidated server-side, so an auth-dead Session can continue — the frontend clears
649
+ * its auth-dead composer state immediately. Published to every existing Session channel
650
+ * of the Project; tabs without a live channel learn the same fact from the models
651
+ * response's `updatedAt` on their next load.
652
+ */
653
+ | {
654
+ type: "credentials_updated";
655
+ }
547
656
  /** Placeholder handshake on the user channel (reserved for automated task notifications). */
548
657
  | {
549
658
  type: "hello";
@@ -555,7 +664,32 @@ type ServerEvent =
555
664
  agentId: string;
556
665
  sessionId: string;
557
666
  source: SessionSource;
558
- } | ScheduleServerEvent;
667
+ } | ScheduleServerEvent | GoalServerEvent;
668
+ /** Goal-mode progress on the session channel (the chat page drives its goal banner from these). */
669
+ type GoalServerEvent =
670
+ /** A goal run began (published before the first round). */
671
+ {
672
+ type: "goal_started";
673
+ sessionId: string;
674
+ objective: string;
675
+ budget: number;
676
+ }
677
+ /** A round is starting; `used` is the runner's accounting up to this point. */
678
+ | {
679
+ type: "goal_round";
680
+ sessionId: string;
681
+ round: number;
682
+ used: number;
683
+ budget: number;
684
+ }
685
+ /** The goal reached a terminal state. */
686
+ | {
687
+ type: "goal_finished";
688
+ sessionId: string;
689
+ outcome: "complete" | "blocked" | "budget_limited" | "aborted";
690
+ rounds: number;
691
+ used: number;
692
+ };
559
693
  /** Schedule notification (user-level event stream; firing and delivery are notified via /api/events). */
560
694
  type ScheduleServerEvent =
561
695
  /** Fired and sent (sessionId is the session that received the Prompt; a new session under new-Session mode). */
@@ -790,6 +924,18 @@ interface AgentTraceDateGroup {
790
924
  interface AgentTracesResponse {
791
925
  dates: AgentTraceDateGroup[];
792
926
  }
927
+ interface TraceImportRequest {
928
+ /** Base64 of the Trace file content (JSON Lines; the first record must be `session_meta`). */
929
+ dataBase64: string;
930
+ }
931
+ interface TraceImportResponse {
932
+ /** Session id taken from the imported file's `session_meta`. */
933
+ sessionId: string;
934
+ /** Allocated file index: always 1 — an import creates a new Session (a duplicate session id is rejected with 409 `trace_session_exists`). */
935
+ index: number;
936
+ /** Date directory the file landed in (local yyyy-mm-dd from the first record's timestamp, matching the Trace Writer's convention). */
937
+ date: string;
938
+ }
793
939
  type UsageGroupBy = "date" | "agent" | "model" | "session";
794
940
  interface UsageBucket {
795
941
  total: number;
@@ -871,9 +1017,20 @@ interface UsageErrors {
871
1017
  unexpected: number;
872
1018
  /** The most frequent source · code (null when there are no errors). */
873
1019
  topCode: UsageErrorCount | null;
874
- /** Most recent N items (reverse chronological). */
1020
+ /** Most recent N items (reverse chronological) — the first page; older ones come from `GET /usage/errors`. */
875
1021
  recent: UsageErrorItem[];
876
1022
  }
1023
+ /**
1024
+ * GET /api/projects/:projectId/usage/errors — one page of the error detail table, newest
1025
+ * first. The dashboard response above already carries the first page; this exists so
1026
+ * "show me earlier ones" does not have to refetch the whole aggregate. It takes the same
1027
+ * date/agent filter as the dashboard, so a page never widens what the summary counted.
1028
+ */
1029
+ interface UsageErrorsPage {
1030
+ items: UsageErrorItem[];
1031
+ /** Filtered row count, so the caller knows when it has reached the end. */
1032
+ total: number;
1033
+ }
877
1034
  interface UsageResponse {
878
1035
  summary: {
879
1036
  today: UsageBucket;
@@ -961,21 +1118,22 @@ interface AgentImportResponse {
961
1118
  /** Raw result of a single run (a scoreboard per-case runs[] entry). */
962
1119
  interface BenchmarkRunScore {
963
1120
  score: number;
964
- cost?: number;
965
- durationMs?: number;
1121
+ /** Run cost, or null when unavailable. */
1122
+ cost: number | null;
1123
+ durationMs: number;
966
1124
  /** Id of the Session under test in this run (links to Trace). */
967
- sessionId?: string;
1125
+ sessionId: string;
968
1126
  }
969
1127
  interface BenchmarkCaseScore {
970
1128
  case: string;
971
- /** Per-case score = average of runs (equals that single run's score under the legacy single-run format). */
1129
+ /** Model-written average of this Case's Run scores, on the fixed 0..100 scale. */
972
1130
  score: number;
973
- cost?: number;
974
- durationMs?: number;
975
- /** For legacy format compatibility: per-case single Session id (new format keeps it inside runs[]). */
976
- sessionId?: string;
977
- /** Raw results per run; unset under the legacy format (the server backfills one entry when parsing as a single run). */
978
- runs?: BenchmarkRunScore[];
1131
+ /** Model-written average of known Run costs; null when every Run cost is unknown. */
1132
+ cost: number | null;
1133
+ /** Model-written average of Run durations, rounded to an integer. */
1134
+ durationMs: number;
1135
+ /** Raw results per Run. */
1136
+ runs: BenchmarkRunScore[];
979
1137
  }
980
1138
  interface BenchmarkEvaluation {
981
1139
  /** Evaluation timestamp (ISO 8601). */
@@ -985,15 +1143,19 @@ interface BenchmarkEvaluation {
985
1143
  /** Evaluation summary body: how the score was derived, what optimizations were made to the Agent this round (required when generating, tolerated as unset when displaying). */
986
1144
  summary?: string;
987
1145
  /** Model actually used for this evaluation round (upstream id, paired with provider; the chart series is split by model). */
988
- modelId?: string;
1146
+ modelId: string;
989
1147
  /** Provider group for `modelId`. */
990
- provider?: string;
1148
+ provider: string;
1149
+ /** Thinking level read from the unchanged Target Agent configuration. */
1150
+ thinkingLevel: string;
991
1151
  /** Agent State version number under test. */
992
- version?: number;
993
- /** Total score (sum of per-case scores; max score defined by the scoring rubric). */
1152
+ version: number;
1153
+ /** Model-written average of Case scores, on the fixed 0..100 scale. */
994
1154
  score: number;
995
- cost?: number;
996
- durationMs?: number;
1155
+ /** Model-written average of known Case costs; null when every Case cost is unknown. */
1156
+ cost: number | null;
1157
+ /** Model-written average of Case durations, rounded to an integer. */
1158
+ durationMs: number;
997
1159
  cases: BenchmarkCaseScore[];
998
1160
  }
999
1161
  interface BenchmarkSummary {
@@ -1012,6 +1174,15 @@ interface BenchmarkSummary {
1012
1174
  interface BenchmarksResponse {
1013
1175
  benchmarks: BenchmarkSummary[];
1014
1176
  }
1177
+ /** Public Benchmark Case metadata. Rubric and Gold content are never included. */
1178
+ interface BenchmarkCaseSummary {
1179
+ id: string;
1180
+ /** First Markdown heading with an optional leading "Case N:" removed; falls back to id. */
1181
+ title: string;
1182
+ }
1183
+ interface BenchmarkCasesResponse {
1184
+ cases: BenchmarkCaseSummary[];
1185
+ }
1015
1186
  interface SkillMetadataItem {
1016
1187
  /** Skill directory name (the identity key for install / uninstall / Prompt addressing). */
1017
1188
  name: string;
@@ -1045,5 +1216,53 @@ interface AgentSkillsResponse {
1045
1216
  interface SkillInstallRequest {
1046
1217
  names: string[];
1047
1218
  }
1219
+ /** GET /api/version: the running server's release identity (from core's VERSION / BUILD_DATE). */
1220
+ interface VersionResponse {
1221
+ version: string;
1222
+ /**
1223
+ * The **running** version's release date (UTC yyyy-mm-dd), stamped into core's
1224
+ * BUILD_DATE at build time by the release workflow — the web's "last updated" date
1225
+ * needs no network. Null for a dev/source build and for releases that predate the
1226
+ * stamping (v0.1.2 and earlier): the UI then shows the version alone.
1227
+ */
1228
+ buildDate: string | null;
1229
+ }
1230
+ /**
1231
+ * GET /api/version/update-check: newest published release vs the running version.
1232
+ * Always HTTP 200 (fail-soft): a lookup failure sets `error` and leaves `latestVersion`
1233
+ * null rather than failing the request; results are cached server-side.
1234
+ */
1235
+ interface UpdateCheckResponse {
1236
+ currentVersion: string;
1237
+ /** Same as VersionResponse.buildDate: the running version's release date, stamped at build time. */
1238
+ buildDate: string | null;
1239
+ /** Newest published release (normalized, no leading `v`); null when the lookup failed or checks are disabled. */
1240
+ latestVersion: string | null;
1241
+ updateAvailable: boolean;
1242
+ /** Release page of the newest release (for the "release notes" link). */
1243
+ releaseUrl: string | null;
1244
+ /** Publish timestamp of the newest release (ISO 8601). */
1245
+ publishedAt: string | null;
1246
+ /** When this result was produced (ISO 8601) — a cached result keeps its original timestamp. */
1247
+ checkedAt: string;
1248
+ /** Present (true) when update checks are turned off via PENGUIN_UPDATE_CHECK=off; no network call was made. */
1249
+ disabled?: true;
1250
+ /** Why the lookup failed: unreachable network / GitHub rate limit / unusable response. */
1251
+ error?: "network" | "rate_limited" | "bad_response";
1252
+ }
1253
+ /**
1254
+ * POST /api/version/update (admin only): runs the CLI self-update (`penguin update --yes`)
1255
+ * on the server host. `unsupported` covers both a server not launched via the CLI and the
1256
+ * CLI's own refusals (source checkout, unrecognized install layout, Windows).
1257
+ */
1258
+ interface UpdateRunResponse {
1259
+ status: "updated" | "failed" | "unsupported";
1260
+ /** Set when the server cannot run the CLI at all (started without `penguin server|web`). */
1261
+ reason?: "not_launched_via_cli";
1262
+ /** Tail of the update command's combined stdout+stderr (capped; empty when nothing ran). */
1263
+ output: string;
1264
+ /** True when the install changed (or was already current): restart the service to run the new version. */
1265
+ needsRestart: boolean;
1266
+ }
1048
1267
 
1049
- export type { AdminPasswordResetRequest, AdminUserCreateRequest, AdminUserCreateResponse, AdminUsersResponse, AgentCompactionConfigDto, AgentConfigDto, AgentConfigResponse, AgentConfigUpdateRequest, AgentCreateRequest, AgentCreateResponse, AgentImportRequest, AgentImportResponse, AgentModelConfigDto, AgentSkillsResponse, AgentSummary, AgentTraceDateGroup, AgentTraceFileRef, AgentTraceSessionGroup, AgentTracesResponse, AgentsResponse, ApprovalDecisionRequest, ApprovalMode, AuthLoginRequest, AuthResponse, BenchmarkCaseScore, BenchmarkEvaluation, BenchmarkRunScore, BenchmarkSummary, BenchmarksResponse, CredentialInfo, DirEntryInfo, DirListResponse, ErrorBody, FilesStatRequest, FilesStatResponse, MeResponse, MemberAddRequest, MemberAddResponse, MemberInfo, MembersResponse, MessagesResponse, ModelInfo, ModelPricingDto, ModelRefDto, ModelTestRequest, ModelTestResponse, ModelUpdateEntry, ModelsResponse, ModelsUpdateRequest, PasswordChangeRequest, PrefsResponse, ProjectCreateRequest, ProjectCreateResponse, ProjectRole, ProjectSummary, ProjectsResponse, RequestSpan, ScheduleItem, ScheduleServerEvent, ScheduleStatus, ScheduleUpsertRequest, SchedulesResponse, ServerEvent, SessionCategory, SessionCategoryCounts, SessionCreateRequest, SessionCreateResponse, SessionInfo, SessionPatchRequest, SessionResponse, SessionSource, SessionStatus, SessionTracesResponse, SessionsResponse, SkillGroupItem, SkillInstallRequest, SkillLibraryResponse, SkillMetadataItem, SteerRequest, TaskCreateRequest, TaskCreateResponse, TaskInputPart, ToolCallSpan, TraceAnalysisResponse, TraceEventsResponse, TraceFileInfo, TraceModelSegment, TraceTaskStats, TraceToolSpan, UiPrefs, UsageAgentCount, UsageBucket, UsageErrorCount, UsageErrorItem, UsageErrors, UsageGroupBy, UsageGroupRow, UsageResponse, UsageSuccessRate, UsageTrendPoint, UsageTrendPointInTrace, UserInfo, VaultEntryInfo, VaultEntryUpdate, VaultResponse, VaultUpdateRequest, WorkspaceFileEntry, WorkspaceFilesResponse };
1268
+ export type { AdminPasswordResetRequest, AdminUserCreateRequest, AdminUserCreateResponse, AdminUsersResponse, AgentCompactionConfigDto, AgentConfigDto, AgentConfigResponse, AgentConfigUpdateRequest, AgentCreateRequest, AgentCreateResponse, AgentImportRequest, AgentImportResponse, AgentModelConfigDto, AgentSkillsResponse, AgentSummary, AgentTraceDateGroup, AgentTraceFileRef, AgentTraceSessionGroup, AgentTracesResponse, AgentsResponse, ApprovalDecisionRequest, ApprovalMode, AuthLoginRequest, AuthResponse, BenchmarkCaseScore, BenchmarkCaseSummary, BenchmarkCasesResponse, BenchmarkEvaluation, BenchmarkRunScore, BenchmarkSummary, BenchmarksResponse, CredentialInfo, DirEntryInfo, DirListResponse, ErrorBody, FilesStatRequest, FilesStatResponse, GoalResponse, GoalServerEvent, GoalStateView, MeResponse, MemberAddRequest, MemberAddResponse, MemberInfo, MembersResponse, MessagesLiveTail, MessagesResponse, ModelInfo, ModelPricingDto, ModelRefDto, ModelTestRequest, ModelTestResponse, ModelUpdateEntry, ModelsResponse, ModelsUpdateRequest, PasswordChangeRequest, PrefsResponse, ProjectCreateRequest, ProjectCreateResponse, ProjectRole, ProjectSummary, ProjectUpdateRequest, ProjectUpdateResponse, ProjectsResponse, RequestSpan, RetryNowResponse, ScheduleItem, ScheduleServerEvent, ScheduleStatus, ScheduleUpsertRequest, SchedulesResponse, ServerEvent, SessionCategory, SessionCategoryCounts, SessionCreateRequest, SessionCreateResponse, SessionInfo, SessionPatchRequest, SessionResponse, SessionSource, SessionStatus, SessionTracesResponse, SessionsResponse, SkillGroupItem, SkillInstallRequest, SkillLibraryResponse, SkillMetadataItem, SteerRequest, TaskCreateRequest, TaskCreateResponse, TaskInputPart, ToolCallSpan, TraceAnalysisResponse, TraceEventsResponse, TraceFileInfo, TraceImportRequest, TraceImportResponse, TraceModelSegment, TraceTaskStats, TraceToolSpan, UiPrefs, UpdateCheckResponse, UpdateRunResponse, UsageAgentCount, UsageBucket, UsageErrorCount, UsageErrorItem, UsageErrors, UsageErrorsPage, UsageGroupBy, UsageGroupRow, UsageResponse, UsageSuccessRate, UsageTrendPoint, UsageTrendPointInTrace, UserInfo, VaultEntryInfo, VaultEntryUpdate, VaultResponse, VaultUpdateRequest, VersionResponse, WorkspaceFileEntry, WorkspaceFilesResponse };