Skip to content
Source
Sign inSign up

prompt-link-model.ts

93 lines3.4 KB

1import type { PromptLinkRepository } from "@t3tools/shared/promptLinks";
2​
3const PROMPT_LINK_PARAM_KEYS = ["prompt", "repo", "pr", "provider", "model"] as const;
4​
5type RouteParams = Readonly<Record<string, unknown>> | undefined;
6​
7export function promptLinkSearchFromParams(
8 params: RouteParams,
9 keys: ReadonlyArray<string> = PROMPT_LINK_PARAM_KEYS,
10): string | null {
11 const search = new URLSearchParams();
12 for (const key of keys) {
13 const raw = params?.[key];
14 const value = Array.isArray(raw) ? raw[0] : raw;
15 if (typeof value === "string") search.set(key, value);
16 }
17 return search.size > 0 ? search.toString() : null;
19​
20export function withoutPromptLinkParams(params: object | undefined): Record<string, unknown> {
21 return Object.fromEntries(
22 Object.entries(params ?? {}).filter(
23 ([key]) => !(PROMPT_LINK_PARAM_KEYS as ReadonlyArray<string>).includes(key),
24 ),
25 );
27​
28export function promptLinkNotice(input: {
29 readonly hiddenCharacters: number;
30 readonly modelUnavailable?: boolean;
31 readonly pullRequestUnavailable?: number | null;
32}): string {
33 return [
34 input.hiddenCharacters > 0
35 ? "This prompt had hidden characters, it may be malicious. They were removed. Read it before you send."
36 : "This prompt came from a link. Read it before you send.",
37 input.modelUnavailable
38 ? "The requested model is not available, so your default is used."
39 : null,
40 input.pullRequestUnavailable != null
41 ? `Pull request #${input.pullRequestUnavailable} could not be loaded, so this task starts from your default branch.`
42 : null,
43 ]
44 .filter((line) => line !== null)
45 .join("\n\n");
47​
48export function linkedThreadDraftAction(currentText: string, prompt: string) {
49 if (currentText.trim().length === 0) return "fill" as const;
50 return currentText === prompt ? ("keep" as const) : ("ask" as const);
52​
53export function promptLinkScratchEnvironmentId<Id extends string>(
54 environmentIdsMostRecentFirst: ReadonlyArray<Id>,
55 scratchEnvironmentIds: ReadonlyArray<Id>,
56): Id | null {
57 return (
58 environmentIdsMostRecentFirst.find((id) => scratchEnvironmentIds.includes(id)) ??
59 scratchEnvironmentIds[0] ??
60 null
61 );
63​
64export function latestThreadEnvironmentIds<Id extends string>(
65 threads: ReadonlyArray<{ readonly environmentId: Id; readonly updatedAt: string }>,
66): ReadonlyArray<Id> {
67 const latest = new Map<Id, string>();
68 for (const thread of threads) {
69 if (thread.updatedAt > (latest.get(thread.environmentId) ?? "")) {
70 latest.set(thread.environmentId, thread.updatedAt);
71 }
72 }
73 return Array.from(latest.keys()).sort((left, right) =>
74 (latest.get(right) ?? "").localeCompare(latest.get(left) ?? ""),
75 );
77​
78export const promptLinkRepositoryName = (repository: PromptLinkRepository) =>
79 `${repository.owner}/${repository.name}`;
80​
81/**
82 * Advances a parsed link at each navigation state; `resolve` is the link to apply. The first
83 * state after the parse must hold the link's screen. A link that never lands there, or whose
84 * screen is left before its target is ready, is dropped so it cannot apply on a later visit.
85 */
86export function stepPendingPromptLink<Link>(
87 link: Link,
88 input: { readonly screenOnStack: boolean; readonly ready: boolean },
89): { readonly pending: Link | null; readonly resolve: Link | null } {
90 if (!input.screenOnStack) return { pending: null, resolve: null };
91 if (input.ready) return { pending: null, resolve: link };
92 return { pending: link, resolve: null };