@prismshadow/penguin-server 0.1.1 → 0.1.4

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.
@@ -48,6 +48,15 @@ interface AuthResponse {
48
48
  }
49
49
  interface MeResponse {
50
50
  user: UserInfo;
51
+ /**
52
+ * Whether Workspace HTML previews open on a separate origin (see design §
53
+ * "Workspace 文件预览"). False means this deployment has no usable preview origin —
54
+ * the App is reached on something other than a loopback name and
55
+ * PENGUIN_PREVIEW_ORIGIN is unset — so previews fall back to the same-origin sandbox,
56
+ * where `localStorage`, cookies and third-party embeds do not work. Computed per
57
+ * request, since it depends on the host the caller is using.
58
+ */
59
+ previewIsolated: boolean;
51
60
  }
52
61
  interface PasswordChangeRequest {
53
62
  oldPassword: string;
@@ -177,6 +186,13 @@ interface ModelsResponse {
177
186
  defaultModel?: ModelRefDto;
178
187
  /** Vision model used as a proxy reader for read_image (describes images when the session model has vision=false). */
179
188
  visionModel?: ModelRefDto;
189
+ /**
190
+ * When the Project's model/credential config last changed (ISO; the config file's mtime,
191
+ * so it survives restarts). The web's auth-dead gate compares it against the last auth
192
+ * abort: an abort OLDER than the last credential update no longer disables the composer
193
+ * (the key was fixed since). Absent when the Project has no config file yet.
194
+ */
195
+ updatedAt?: string;
180
196
  models: ModelInfo[];
181
197
  }
182
198
  /** PUT full-table replace semantics: models not present are deleted; omitting apiKey = keep existing value. Key = (provider, modelId). */
@@ -382,13 +398,40 @@ interface SessionInfo {
382
398
  status: SessionStatus;
383
399
  /** Number of approvals awaiting human decision (a persisted count outside server events, for list badges). */
384
400
  pendingApprovalCount: number;
401
+ /** Number of queued follow-up tasks (`queueIfBusy`) awaiting auto-start once the session is idle. */
402
+ pendingFollowUpCount: number;
385
403
  /** Whether a Trace record exists (a Task has been started). */
386
404
  hasTrace: boolean;
387
405
  /** Whether archived (hidden from the default list, grouped under "Archived"). */
388
406
  archived: boolean;
407
+ /**
408
+ * Absolute path of the session's latest Trace file (the current context shard); absent
409
+ * when no Trace exists yet. Populated on the **single-session GET only** — list rows omit
410
+ * it (locating it costs a directory walk per Session). The web's `/model` switch puts it
411
+ * into the new session's `[model_switch_from]` block so the model can read the source
412
+ * history itself when it needs it.
413
+ */
414
+ tracePath?: string;
389
415
  }
416
+ /**
417
+ * Session list category, the sidebar's four-way split applied server-side: archived wins
418
+ * regardless of origin (archiving is an explicit user action), then the origin's bucket,
419
+ * and a Session with no (or an unknown) source is `active` — user-created rows.
420
+ */
421
+ type SessionCategory = "active" | SessionSource | "archived";
422
+ /** Per-category totals across an Agent's whole Session list (returned when the list is requested with counts). */
423
+ type SessionCategoryCounts = Record<SessionCategory, number>;
390
424
  interface SessionsResponse {
391
425
  sessions: SessionInfo[];
426
+ /** Present when the request asked for counts (`counts=1`): totals per category over the full list, not just the returned page. */
427
+ counts?: SessionCategoryCounts;
428
+ /**
429
+ * Present with `counts`: the same totals broken down by Workspace path (only paths
430
+ * with at least one Session appear). The sidebar's workspace grouping decides each
431
+ * group's folders and "More" from its own share, so a group never advertises
432
+ * content that lives in other Workspaces.
433
+ */
434
+ workspaceCounts?: Record<string, SessionCategoryCounts>;
392
435
  }
393
436
  /** Server directory browsing (advanced new-Workspace picker): starts from the home directory by default, can navigate up to the root. */
394
437
  interface DirEntryInfo {
@@ -431,9 +474,41 @@ interface SessionPatchRequest {
431
474
  /** Manual rename; non-empty string, overrides the auto-generated title. */
432
475
  title?: string;
433
476
  }
477
+ /**
478
+ * Live in-progress tail of a running Session, carried by `MessagesResponse.live`.
479
+ *
480
+ * Contract (see runtime/live-tail.ts and the GET /messages route): the server captures
481
+ * `cursor` and `fragments` atomically — in one synchronous tick, before starting the
482
+ * trace read — while the Session is running/compacting.
483
+ * - `cursor`: the Session channel's most recently assigned SSE event id
484
+ * (`<epoch>-<seq>`); every event published up to and including this id is already
485
+ * reflected in `fragments`.
486
+ * - `fragments`: one synthetic `partial_* start` OmniMessage per open streaming
487
+ * fragment, whose payload carries the full accumulated content so far (text/thinking
488
+ * prefix, tool-call name + accumulated arguments, tool-output prefix + images), with
489
+ * the original `origin` chain preserved.
490
+ *
491
+ * Client usage (the bundled Web App's connect-first flow): after applying `messages`,
492
+ * when the cursor's epoch matches the epoch of the SSE events seen on the current
493
+ * connection, drop every buffered **partial** event with seq <= cursor (its content is
494
+ * already inside `fragments`), feed `fragments` through the normal reducer path, then
495
+ * replay the rest of the buffer. Buffered **complete** messages are never dropped by the
496
+ * cursor — the regular overlap dedup decides for them — so nothing is lost even when a
497
+ * complete message's trace append is still in flight at read time.
498
+ */
499
+ interface MessagesLiveTail {
500
+ cursor: string;
501
+ fragments: OmniMessage[];
502
+ }
434
503
  /** Message history: the full messages and events from concatenating all of this Session's Trace files in order (excludes partial_*). */
435
504
  interface MessagesResponse {
436
505
  messages: OmniMessage[];
506
+ /**
507
+ * Present only while the Session is running/compacting: the in-progress stream tail
508
+ * (open streaming fragments + the channel cursor they cover), so a client joining
509
+ * mid-stream can render the currently streaming message. Omitted when idle.
510
+ */
511
+ live?: MessagesLiveTail;
437
512
  }
438
513
  /**
439
514
  * A single Prompt's input parts: text or image (data: / http(s) URL).
@@ -448,14 +523,71 @@ type TaskInputPart = {
448
523
  };
449
524
  interface TaskCreateRequest {
450
525
  input: TaskInputPart[];
526
+ /**
527
+ * Thinking level for this Task's LLM requests (a per-turn parameter; one of
528
+ * `none | low | medium | high | xhigh`, anything else is a 400). Omitted = falls back to
529
+ * the session's default (the Agent config's `model.thinking_level`). A queued follow-up
530
+ * (`queueIfBusy`) keeps its level and applies it when it auto-starts.
531
+ */
532
+ thinkingLevel?: ThinkingLevelName;
533
+ /**
534
+ * Queue instead of 409 when a Task/compaction is already in progress: the input is held
535
+ * server-side and auto-starts as an ordinary next task once the session returns to idle
536
+ * (in queue order, one at a time). The response then carries `queued: true`.
537
+ */
538
+ queueIfBusy?: boolean;
539
+ /**
540
+ * Present = goal mode: the input's text becomes the objective (leading `[use_skills]`
541
+ * blocks and the like are stripped from the recorded objective; the round-1 message keeps
542
+ * them) and the server loops the Session until the goal reaches a terminal state.
543
+ * `budget` is the token budget (uncached input + output); omitted or -1 = unlimited.
544
+ */
545
+ goal?: {
546
+ budget?: number;
547
+ };
548
+ }
549
+ /** Goal-mode run state (from goal_state; the chat page's banner restores from the latest row). */
550
+ interface GoalStateView {
551
+ objective: string;
552
+ status: "active" | "complete" | "blocked" | "budget_limited" | "aborted";
553
+ /** Token budget; -1 = unlimited. */
554
+ budget: number;
555
+ used: number;
556
+ rounds: number;
557
+ updatedAt: string;
558
+ }
559
+ interface GoalResponse {
560
+ /** The Session's most recent goal run; null if it never ran one. */
561
+ goal: GoalStateView | null;
451
562
  }
452
563
  interface TaskCreateResponse {
453
564
  /** Current actual session_id: a Trace-less invalid Session self-heals and returns a new id; the frontend updates its route accordingly. */
454
565
  sessionId: string;
566
+ /** True when `queueIfBusy` enqueued the input as a follow-up instead of starting it (absent/false: the task started). */
567
+ queued?: boolean;
568
+ }
569
+ /**
570
+ * Mid-run steering (POST /api/sessions/:id/steer): a user message for the **running** Task,
571
+ * delivered by core between turns as a standalone `[user_steering]` user message. 202 on
572
+ * queue; 409 `not_running` when no Task is in progress (the frontend falls back to a normal
573
+ * task POST).
574
+ */
575
+ interface SteerRequest {
576
+ /** Non-empty message text (trimmed server-side). */
577
+ text: string;
455
578
  }
456
579
  interface ApprovalDecisionRequest {
457
580
  decision: "allow" | "deny";
458
581
  }
582
+ /**
583
+ * POST /api/sessions/:sessionId/retry-now — skip the in-progress reconnect backoff and
584
+ * fire the next retry immediately (the "retry now" button on the reconnect countdown).
585
+ * `skipped: false` is the benign "no reconnect wait in progress" case (idle session, or
586
+ * the wait elapsed in a timing race), not an error.
587
+ */
588
+ interface RetryNowResponse {
589
+ skipped: boolean;
590
+ }
459
591
  /** Docs: /docs/server-api § "Streaming (SSE)". */
460
592
  type ServerEvent =
461
593
  /**
@@ -467,10 +599,11 @@ type ServerEvent =
467
599
  toolCall: OmniMessage<ToolCallPayload>;
468
600
  origin?: string[];
469
601
  }
470
- /** Session run status flip (for toggling the input area and list). */
602
+ /** Session run status flip (for toggling the input area and list); `queued` = queued follow-up count (see TaskCreateRequest.queueIfBusy). */
471
603
  | {
472
604
  type: "task_state";
473
605
  state: SessionStatus;
606
+ queued?: number;
474
607
  }
475
608
  /** The model-generated title after the first turn has been persisted (for in-place list updates). */
476
609
  | {
@@ -482,6 +615,16 @@ type ServerEvent =
482
615
  | {
483
616
  type: "resync_required";
484
617
  }
618
+ /**
619
+ * The Project's model credentials changed (PUT /models): cached runtimes have been
620
+ * invalidated server-side, so an auth-dead Session can continue — the frontend clears
621
+ * its auth-dead composer state immediately. Published to every existing Session channel
622
+ * of the Project; tabs without a live channel learn the same fact from the models
623
+ * response's `updatedAt` on their next load.
624
+ */
625
+ | {
626
+ type: "credentials_updated";
627
+ }
485
628
  /** Placeholder handshake on the user channel (reserved for automated task notifications). */
486
629
  | {
487
630
  type: "hello";
@@ -493,7 +636,32 @@ type ServerEvent =
493
636
  agentId: string;
494
637
  sessionId: string;
495
638
  source: SessionSource;
496
- } | ScheduleServerEvent;
639
+ } | ScheduleServerEvent | GoalServerEvent;
640
+ /** Goal-mode progress on the session channel (the chat page drives its goal banner from these). */
641
+ type GoalServerEvent =
642
+ /** A goal run began (published before the first round). */
643
+ {
644
+ type: "goal_started";
645
+ sessionId: string;
646
+ objective: string;
647
+ budget: number;
648
+ }
649
+ /** A round is starting; `used` is the runner's accounting up to this point. */
650
+ | {
651
+ type: "goal_round";
652
+ sessionId: string;
653
+ round: number;
654
+ used: number;
655
+ budget: number;
656
+ }
657
+ /** The goal reached a terminal state. */
658
+ | {
659
+ type: "goal_finished";
660
+ sessionId: string;
661
+ outcome: "complete" | "blocked" | "budget_limited" | "aborted";
662
+ rounds: number;
663
+ used: number;
664
+ };
497
665
  /** Schedule notification (user-level event stream; firing and delivery are notified via /api/events). */
498
666
  type ScheduleServerEvent =
499
667
  /** Fired and sent (sessionId is the session that received the Prompt; a new session under new-Session mode). */
@@ -582,7 +750,7 @@ interface TraceTaskStats {
582
750
  /**
583
751
  * This turn's duration span: `startTs` = the moment of this turn's **first `request_begin`**
584
752
  * — duration only looks at LLM requests, not the timestamp of user text like the user
585
- * Prompt / compaction summary (`<context_summary>` is created during compaction but only
753
+ * Prompt / compaction summary (`[context_summary]` is created during compaction but only
586
754
  * persisted on the next run; resuming the next day would inflate the first turn by a whole
587
755
  * day for no reason); `endTs` = the moment of the last non-session_meta message in the
588
756
  * range. For a degenerate turn with no Request at all (interrupted right after sending),
@@ -728,6 +896,18 @@ interface AgentTraceDateGroup {
728
896
  interface AgentTracesResponse {
729
897
  dates: AgentTraceDateGroup[];
730
898
  }
899
+ interface TraceImportRequest {
900
+ /** Base64 of the Trace file content (JSON Lines; the first record must be `session_meta`). */
901
+ dataBase64: string;
902
+ }
903
+ interface TraceImportResponse {
904
+ /** Session id taken from the imported file's `session_meta`. */
905
+ sessionId: string;
906
+ /** Allocated file index: always 1 — an import creates a new Session (a duplicate session id is rejected with 409 `trace_session_exists`). */
907
+ index: number;
908
+ /** Date directory the file landed in (local yyyy-mm-dd from the first record's timestamp, matching the Trace Writer's convention). */
909
+ date: string;
910
+ }
731
911
  type UsageGroupBy = "date" | "agent" | "model" | "session";
732
912
  interface UsageBucket {
733
913
  total: number;
@@ -983,5 +1163,53 @@ interface AgentSkillsResponse {
983
1163
  interface SkillInstallRequest {
984
1164
  names: string[];
985
1165
  }
1166
+ /** GET /api/version: the running server's release identity (from core's VERSION / BUILD_DATE). */
1167
+ interface VersionResponse {
1168
+ version: string;
1169
+ /**
1170
+ * The **running** version's release date (UTC yyyy-mm-dd), stamped into core's
1171
+ * BUILD_DATE at build time by the release workflow — the web's "last updated" date
1172
+ * needs no network. Null for a dev/source build and for releases that predate the
1173
+ * stamping (v0.1.2 and earlier): the UI then shows the version alone.
1174
+ */
1175
+ buildDate: string | null;
1176
+ }
1177
+ /**
1178
+ * GET /api/version/update-check: newest published release vs the running version.
1179
+ * Always HTTP 200 (fail-soft): a lookup failure sets `error` and leaves `latestVersion`
1180
+ * null rather than failing the request; results are cached server-side.
1181
+ */
1182
+ interface UpdateCheckResponse {
1183
+ currentVersion: string;
1184
+ /** Same as VersionResponse.buildDate: the running version's release date, stamped at build time. */
1185
+ buildDate: string | null;
1186
+ /** Newest published release (normalized, no leading `v`); null when the lookup failed or checks are disabled. */
1187
+ latestVersion: string | null;
1188
+ updateAvailable: boolean;
1189
+ /** Release page of the newest release (for the "release notes" link). */
1190
+ releaseUrl: string | null;
1191
+ /** Publish timestamp of the newest release (ISO 8601). */
1192
+ publishedAt: string | null;
1193
+ /** When this result was produced (ISO 8601) — a cached result keeps its original timestamp. */
1194
+ checkedAt: string;
1195
+ /** Present (true) when update checks are turned off via PENGUIN_UPDATE_CHECK=off; no network call was made. */
1196
+ disabled?: true;
1197
+ /** Why the lookup failed: unreachable network / GitHub rate limit / unusable response. */
1198
+ error?: "network" | "rate_limited" | "bad_response";
1199
+ }
1200
+ /**
1201
+ * POST /api/version/update (admin only): runs the CLI self-update (`penguin update --yes`)
1202
+ * on the server host. `unsupported` covers both a server not launched via the CLI and the
1203
+ * CLI's own refusals (source checkout, unrecognized install layout, Windows).
1204
+ */
1205
+ interface UpdateRunResponse {
1206
+ status: "updated" | "failed" | "unsupported";
1207
+ /** Set when the server cannot run the CLI at all (started without `penguin server|web`). */
1208
+ reason?: "not_launched_via_cli";
1209
+ /** Tail of the update command's combined stdout+stderr (capped; empty when nothing ran). */
1210
+ output: string;
1211
+ /** True when the install changed (or was already current): restart the service to run the new version. */
1212
+ needsRestart: boolean;
1213
+ }
986
1214
 
987
- 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, SessionCreateRequest, SessionCreateResponse, SessionInfo, SessionPatchRequest, SessionResponse, SessionSource, SessionStatus, SessionTracesResponse, SessionsResponse, SkillGroupItem, SkillInstallRequest, SkillLibraryResponse, SkillMetadataItem, 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 };
1215
+ 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, GoalResponse, GoalServerEvent, GoalStateView, MeResponse, MemberAddRequest, MemberAddResponse, MemberInfo, MembersResponse, MessagesLiveTail, MessagesResponse, ModelInfo, ModelPricingDto, ModelRefDto, ModelTestRequest, ModelTestResponse, ModelUpdateEntry, ModelsResponse, ModelsUpdateRequest, PasswordChangeRequest, PrefsResponse, ProjectCreateRequest, ProjectCreateResponse, ProjectRole, ProjectSummary, 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, UsageGroupBy, UsageGroupRow, UsageResponse, UsageSuccessRate, UsageTrendPoint, UsageTrendPointInTrace, UserInfo, VaultEntryInfo, VaultEntryUpdate, VaultResponse, VaultUpdateRequest, VersionResponse, WorkspaceFileEntry, WorkspaceFilesResponse };