@cat-factory/app 0.183.0 → 0.185.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.
@@ -10,6 +10,7 @@ import type {
10
10
  SandboxExperimentStatus,
11
11
  SandboxFixtureKind,
12
12
  SandboxGrade,
13
+ SandboxPromptOrigin,
13
14
  SandboxPromptVersion,
14
15
  SandboxRun,
15
16
  } from '~/types/sandbox'
@@ -35,6 +36,47 @@ const FIXTURE_KIND_LABEL = computed<Record<SandboxFixtureKind, string>>(() => ({
35
36
  'repo-feature': t('sandbox.fixtureKind.repo-feature'),
36
37
  'repo-bug': t('sandbox.fixtureKind.repo-bug'),
37
38
  }))
39
+ /**
40
+ * Badge colour per prompt origin. An exhaustive Record over the closed union rather than a
41
+ * ternary, so adding an origin fails the typecheck here instead of silently rendering as
42
+ * "candidate" — the drift guard the i18n conventions ask for on enum-keyed lookups.
43
+ */
44
+ const PROMPT_ORIGIN_COLOR: Record<SandboxPromptOrigin, 'neutral' | 'primary' | 'warning'> = {
45
+ baseline: 'neutral',
46
+ candidate: 'primary',
47
+ workspace: 'warning',
48
+ }
49
+
50
+ /** Which version is mid-promotion, so only its button spins. */
51
+ const promoting = ref<string | null>(null)
52
+
53
+ /**
54
+ * Promotion is offered on anything that is not already what the workspace runs: a graded
55
+ * candidate (the point of the tool) and an older workspace revision (rolling back). Not on the
56
+ * live row, where it is a no-op the backend would swallow anyway — and not on a shipped baseline,
57
+ * since "run what the product ships" is the revert in the prompt editor, not a promotion that
58
+ * would pin today's wording as a stored override.
59
+ */
60
+ function canPromote(version: SandboxPromptVersion): boolean {
61
+ return version.origin !== 'baseline' && version.live !== true
62
+ }
63
+
64
+ async function promote(version: SandboxPromptVersion) {
65
+ promoting.value = version.id
66
+ try {
67
+ await store.promotePrompt(version)
68
+ toast.add({
69
+ title: t('sandbox.prompts.promoted', { agent: version.agentKind }),
70
+ color: 'success',
71
+ icon: 'i-lucide-rocket',
72
+ })
73
+ } catch {
74
+ toast.add({ title: t('sandbox.prompts.promoteFailed'), color: 'error' })
75
+ } finally {
76
+ promoting.value = null
77
+ }
78
+ }
79
+
38
80
  const FIXTURE_ORIGIN_LABEL = computed<Record<'builtin' | 'custom', string>>(() => ({
39
81
  builtin: t('sandbox.fixtureOrigin.builtin'),
40
82
  custom: t('sandbox.fixtureOrigin.custom'),
@@ -508,17 +550,20 @@ async function archive(prompt: SandboxPromptVersion) {
508
550
  <div class="min-w-0">
509
551
  <div class="flex items-center gap-2">
510
552
  <span class="truncate text-slate-200">{{ p.name }}</span>
511
- <UBadge
512
- :color="p.origin === 'baseline' ? 'neutral' : 'primary'"
513
- variant="soft"
514
- size="xs"
515
- >
553
+ <UBadge :color="PROMPT_ORIGIN_COLOR[p.origin]" variant="soft" size="xs">
516
554
  {{
517
555
  p.origin === 'baseline'
518
556
  ? t('sandbox.baseline')
519
557
  : t('sandbox.versionLabel', { version: p.version })
520
558
  }}
521
559
  </UBadge>
560
+ <UBadge v-if="p.origin === 'workspace'" color="warning" variant="soft" size="xs">
561
+ {{
562
+ p.live
563
+ ? t('sandbox.prompts.liveInWorkspace')
564
+ : t('sandbox.prompts.fromWorkspace')
565
+ }}
566
+ </UBadge>
522
567
  </div>
523
568
  <span class="text-[11px] text-slate-500">{{ p.agentKind }}</span>
524
569
  </div>
@@ -535,6 +580,19 @@ async function archive(prompt: SandboxPromptVersion) {
535
580
  "
536
581
  @click="edit(p)"
537
582
  />
583
+ <!-- Deploy: make this version the workspace's live prompt for its agent kind.
584
+ Offered on a graded candidate and on an older workspace revision (rolling
585
+ back), but not on the one already live, where it would be a no-op. -->
586
+ <UButton
587
+ v-if="canPromote(p)"
588
+ icon="i-lucide-rocket"
589
+ color="primary"
590
+ variant="ghost"
591
+ size="xs"
592
+ :loading="promoting === p.id"
593
+ :title="t('sandbox.prompts.promoteTitle')"
594
+ @click="promote(p)"
595
+ />
538
596
  <UButton
539
597
  v-if="p.origin === 'candidate'"
540
598
  icon="i-lucide-archive"
@@ -0,0 +1,41 @@
1
+ import {
2
+ getAgentPromptContract,
3
+ listAgentPromptsContract,
4
+ promoteAgentPromptContract,
5
+ saveAgentPromptContract,
6
+ } from '@cat-factory/contracts'
7
+ import type { SaveAgentPromptInput } from '~/types/agent-prompts'
8
+ import type { ApiContext } from './context'
9
+
10
+ /**
11
+ * The workspace's agent system-prompt overrides, edited from the pipeline builder. There is no
12
+ * delete: going back to the shipped prompt is a save with `text: null`, so the history of what
13
+ * a workspace was running is never lost.
14
+ */
15
+ export function agentPromptsApi({ send, ws }: ApiContext) {
16
+ return {
17
+ // The override INDEX (no prompt bodies) — the builder badges its steps from this.
18
+ listAgentPrompts: (workspaceId: string) =>
19
+ send(listAgentPromptsContract, { pathPrefix: ws(workspaceId) }),
20
+
21
+ // One kind's editor state: the shipped text, the effective text, and the revision log.
22
+ getAgentPrompt: (workspaceId: string, agentKind: string) =>
23
+ send(getAgentPromptContract, { pathPrefix: ws(workspaceId), pathParams: { agentKind } }),
24
+
25
+ saveAgentPrompt: (workspaceId: string, agentKind: string, body: SaveAgentPromptInput) =>
26
+ send(saveAgentPromptContract, {
27
+ pathPrefix: ws(workspaceId),
28
+ pathParams: { agentKind },
29
+ body,
30
+ }),
31
+
32
+ // Deploy the sandbox half of the workflow: a graded prompt version becomes the live prompt.
33
+ // The text is read server-side from the version, so what runs is what was graded.
34
+ promoteAgentPrompt: (workspaceId: string, agentKind: string, sandboxPromptVersionId: string) =>
35
+ send(promoteAgentPromptContract, {
36
+ pathPrefix: ws(workspaceId),
37
+ pathParams: { agentKind },
38
+ body: { sandboxPromptVersionId },
39
+ }),
40
+ }
41
+ }
@@ -2,6 +2,7 @@ import type { FragmentOwnerKind } from '~/types/domain'
2
2
  import { createApiClient, createSend, createSendWith } from './api/client'
3
3
  import type { ApiContext } from './api/context'
4
4
  import { accountsApi } from './api/accounts'
5
+ import { agentPromptsApi } from './api/agentPrompts'
5
6
  import { platformObservabilityApi } from './api/platformObservability'
6
7
  import { reportsApi } from './api/reports'
7
8
  import { authApi } from './api/auth'
@@ -134,6 +135,7 @@ export function useApi() {
134
135
  ...specApi(ctx),
135
136
  ...notificationsApi(ctx),
136
137
  ...presetsApi(ctx),
138
+ ...agentPromptsApi(ctx),
137
139
  ...preflightsApi(ctx),
138
140
  ...publicApiKeysApi(ctx),
139
141
  ...sharedStacksApi(ctx),
@@ -187,6 +187,14 @@ const CONFLICT_INFO: Record<Exclude<ConflictReason, BespokeConflictReason>, Conf
187
187
  titleKey: 'errors.reviewFriction.blockedTitle',
188
188
  descriptionKey: 'errors.reviewFriction.blockedToast',
189
189
  },
190
+ // Reachable from this generic lookup only if a prompt save is ever driven from a run-start
191
+ // path; the prompt editor words it itself (it also has to re-seed its textarea from what
192
+ // landed). Mapped regardless — the exhaustive Record is the drift guard, not a hint that
193
+ // every reason arrives here.
194
+ prompt_revision_conflict: {
195
+ titleKey: 'errors.conflict.title.prompt_revision_conflict',
196
+ descriptionKey: 'errors.conflict.description.prompt_revision_conflict',
197
+ },
190
198
  }
191
199
 
192
200
  /**
@@ -0,0 +1,100 @@
1
+ import { defineStore } from 'pinia'
2
+ import { computed, ref } from 'vue'
3
+ import type { AgentPromptDetail, AgentPromptSummary } from '~/types/agent-prompts'
4
+ import { useWorkspaceStore } from '~/stores/workspace'
5
+
6
+ /**
7
+ * The workspace's agent system-prompt overrides — the pipeline builder's prompt editor.
8
+ *
9
+ * Two shapes, deliberately loaded apart. The INDEX (`summaries`) says which agent kinds deviate
10
+ * from what the product ships and is loaded with the builder, because it badges every step. A
11
+ * kind's prompt BODIES (`detail`) are loaded only when the editor for that kind opens: a prompt
12
+ * is thousands of characters and a pipeline has a dozen steps, so folding the bodies into the
13
+ * index would make opening the builder pay for text nobody read.
14
+ */
15
+ export const useAgentPromptsStore = defineStore('agentPrompts', () => {
16
+ const api = useApi()
17
+
18
+ const summaries = ref<AgentPromptSummary[]>([])
19
+ const detail = ref<AgentPromptDetail | null>(null)
20
+ const loadingIndex = ref(false)
21
+ const loadingDetail = ref(false)
22
+ const saving = ref(false)
23
+
24
+ /** Agent kinds whose live prompt replaces the shipped one, for the builder's badges. */
25
+ const customizedKinds = computed(
26
+ () => new Set(summaries.value.filter((s) => s.customized).map((s) => s.agentKind)),
27
+ )
28
+
29
+ function isCustomized(agentKind: string): boolean {
30
+ return customizedKinds.value.has(agentKind)
31
+ }
32
+
33
+ /**
34
+ * Load the override index. Best-effort: the builder is fully usable without it (the badges
35
+ * are an affordance, not the feature), and the endpoint 503s on a deployment that wires no
36
+ * override store at all.
37
+ */
38
+ async function loadIndex() {
39
+ const ws = useWorkspaceStore()
40
+ if (!ws.workspaceId) return
41
+ loadingIndex.value = true
42
+ try {
43
+ summaries.value = await api.listAgentPrompts(ws.requireId())
44
+ } finally {
45
+ loadingIndex.value = false
46
+ }
47
+ }
48
+
49
+ /** Open one kind's editor state. Errors propagate — an editor with no prompt is useless. */
50
+ async function load(agentKind: string) {
51
+ const ws = useWorkspaceStore()
52
+ detail.value = null
53
+ loadingDetail.value = true
54
+ try {
55
+ detail.value = await api.getAgentPrompt(ws.requireId(), agentKind)
56
+ return detail.value
57
+ } finally {
58
+ loadingDetail.value = false
59
+ }
60
+ }
61
+
62
+ /**
63
+ * Append a revision: new text, or `null` to go back to the shipped prompt. The server returns
64
+ * the refreshed detail, so the editor re-renders from the server's view of the log rather
65
+ * than a locally-guessed one — which is what makes a rejected concurrent save (409) leave the
66
+ * user looking at what actually landed.
67
+ */
68
+ async function save(agentKind: string, text: string | null, restoredFrom?: number) {
69
+ const ws = useWorkspaceStore()
70
+ saving.value = true
71
+ try {
72
+ detail.value = await api.saveAgentPrompt(ws.requireId(), agentKind, {
73
+ text,
74
+ ...(restoredFrom !== undefined ? { restoredFrom } : {}),
75
+ })
76
+ await loadIndex()
77
+ return detail.value
78
+ } finally {
79
+ saving.value = false
80
+ }
81
+ }
82
+
83
+ function reset() {
84
+ detail.value = null
85
+ }
86
+
87
+ return {
88
+ summaries,
89
+ detail,
90
+ loadingIndex,
91
+ loadingDetail,
92
+ saving,
93
+ customizedKinds,
94
+ isCustomized,
95
+ loadIndex,
96
+ load,
97
+ save,
98
+ reset,
99
+ }
100
+ })
@@ -111,6 +111,20 @@ export const useSandboxStore = defineStore('sandbox', () => {
111
111
  return saved
112
112
  }
113
113
 
114
+ /**
115
+ * Promote a prompt version to the workspace's live prompt for its agent kind — the deploy half
116
+ * of the sandbox workflow. Reloads so the projected `workspace` rows (and their `live` marker)
117
+ * reflect the new head, and refreshes the prompt-override index the pipeline builder badges from
118
+ * so the two surfaces cannot disagree about what is running.
119
+ */
120
+ async function promotePrompt(version: SandboxPromptVersion) {
121
+ const ws = useWorkspaceStore()
122
+ const detail = await api.promoteAgentPrompt(ws.requireId(), version.agentKind, version.id)
123
+ await load()
124
+ await useAgentPromptsStore().loadIndex()
125
+ return detail
126
+ }
127
+
114
128
  async function archivePrompt(promptId: string) {
115
129
  const ws = useWorkspaceStore()
116
130
  await api.archiveSandboxPrompt(ws.requireId(), promptId)
@@ -164,6 +178,7 @@ export const useSandboxStore = defineStore('sandbox', () => {
164
178
  promptsForKind,
165
179
  fixturesForKind,
166
180
  clonePrompt,
181
+ promotePrompt,
167
182
  saveVersion,
168
183
  archivePrompt,
169
184
  createExperiment,
@@ -86,21 +86,25 @@ export const useValidationChecksStore = defineStore('validationChecks', () => {
86
86
  }
87
87
 
88
88
  /**
89
- * Save a service frame's checks. An EMPTY list clears the config on the backend (the service
90
- * deletes the row), which restores the exact pre-feature behaviour so the local list drops
91
- * the entry rather than keeping an empty one that reads as "configured".
89
+ * Save a service frame's checks and its dependency-prepopulation install. The backend deletes
90
+ * the row only when BOTH are empty (restoring the exact pre-feature behaviour), so the local
91
+ * list mirrors that rule — dropping the entry on an empty save of both, and keeping it for a
92
+ * service that declares only an install. Testing `checks` alone here would evict a live
93
+ * install-only config from the store and report the service as unconfigured until a reload.
92
94
  */
93
95
  async function save(
94
96
  blockId: string,
95
97
  checks: ValidationCheck[],
96
98
  maxAttempts: number,
99
+ dependencyInstall?: string,
97
100
  ): Promise<void> {
98
101
  const ws = useWorkspaceStore()
99
102
  const saved = await api.setServiceValidationConfig(ws.requireId(), blockId, {
100
103
  checks,
101
104
  maxAttempts,
105
+ ...(dependencyInstall ? { dependencyInstall } : {}),
102
106
  })
103
- if (saved.checks.length === 0) dropLocal(blockId)
107
+ if (saved.checks.length === 0 && !saved.dependencyInstall) dropLocal(blockId)
104
108
  else upsertLocal(saved)
105
109
  }
106
110
 
@@ -0,0 +1,13 @@
1
+ // Per-workspace agent system-prompt overrides, mirroring `@cat-factory/contracts`
2
+ // (agent-prompts.ts). An append-only revision log per agent kind: the highest revision is what
3
+ // every run in the workspace sends, and a revision whose `text` is null is the deliberate way
4
+ // back to the prompt the product ships.
5
+ //
6
+ // All wire shapes are sourced from @cat-factory/contracts (single source of truth).
7
+
8
+ export type {
9
+ AgentPromptDetail,
10
+ AgentPromptRevision,
11
+ AgentPromptSummary,
12
+ SaveAgentPromptInput,
13
+ } from '@cat-factory/contracts'
@@ -1433,6 +1433,8 @@
1433
1433
  "validationChecks": {
1434
1434
  "title": "Prüfungen vor dem PR",
1435
1435
  "sectionHint": "Befehle, die nach dem Coder und vor dem Öffnen eines Pull Requests im Checkout laufen. Ein Fehlschlag geht zur Behebung an den Agenten zurück; nur ein fehlerfreier Checkout öffnet einen PR.",
1436
+ "dependencyInstall": "Abhängigkeiten installieren",
1437
+ "dependencyInstallHint": "Läuft, bevor der Agent startet",
1436
1438
  "hint": "Jeder Befehl läuft der Reihe nach mit `sh -c` im Checkout dieses Dienstes. Der Agent soll den Code reparieren, nicht die Prüfung abschwächen.",
1437
1439
  "clear": "Leeren",
1438
1440
  "configNoun": "Prüfungen",
@@ -1452,6 +1454,7 @@
1452
1454
  "action": "Erkennen",
1453
1455
  "hint": "Prüfungen aus dem Repository dieses Dienstes vorschlagen",
1454
1456
  "added": "{count} Prüfung hinzugefügt | {count} Prüfungen hinzugefügt",
1457
+ "installOnly": "Abhängigkeitsinstallation eingetragen",
1455
1458
  "found": "Erkannt: {ecosystems}.",
1456
1459
  "capped": "Einige Vorschläge wurden ausgelassen — ein Dienst nimmt höchstens {max} Prüfungen auf.",
1457
1460
  "nothingNew": "Nichts hinzuzufügen",
@@ -3604,7 +3607,9 @@
3604
3607
  "skillNoneAvailable": "Keine Skills verfügbar. Verknüpfe eine Skill-Quelle in den Kontoeinstellungen.",
3605
3608
  "skillMissing": "Dieser Skill ist nicht mehr im Katalog; wähle einen anderen.",
3606
3609
  "skillNeedsPick": "Ein Skill-Schritt braucht einen ausgewählten Skill, bevor du speichern kannst.",
3607
- "purposeStepsConflict": "Dieser Zweck schreibt keinen Code und führt keine Tests aus, doch die Pipeline enthält noch Implementierungs- oder Testschritte. Entfernen Sie diese oder setzen Sie den Zweck auf Entwicklung."
3610
+ "purposeStepsConflict": "Dieser Zweck schreibt keinen Code und führt keine Tests aus, doch die Pipeline enthält noch Implementierungs- oder Testschritte. Entfernen Sie diese oder setzen Sie den Zweck auf Entwicklung.",
3611
+ "promptEditTooltip": "Systemprompt dieses Agenten bearbeiten",
3612
+ "promptEditedTooltip": "Der Systemprompt dieses Agenten ist für diesen Arbeitsbereich bearbeitet"
3608
3613
  },
3609
3614
  "progress": {
3610
3615
  "status": {
@@ -3678,6 +3683,41 @@
3678
3683
  }
3679
3684
  }
3680
3685
  },
3686
+ "agentPrompt": {
3687
+ "title": "Systemprompt: {agent}",
3688
+ "description": "Ersetzt die Anweisung dieses Agenten für alle Läufe in diesem Arbeitsbereich.",
3689
+ "customized": "Bearbeitet",
3690
+ "usingBuiltin": "Standard",
3691
+ "managedNotice": "Die Plattform hängt an alles, was Sie hier speichern, ihre eigenen, nicht bearbeitbaren Regeln an.",
3692
+ "showAppended": "Angehängten Text anzeigen",
3693
+ "hideAppended": "Angehängten Text ausblenden",
3694
+ "appendedHeading": "Von der Plattform angehängt (nicht bearbeitbar)",
3695
+ "placeholder": "Beschreiben Sie, was dieser Agent tun soll.",
3696
+ "save": "Als neue Version speichern",
3697
+ "revert": "Zurück zum Standard",
3698
+ "loadBuiltin": "Standardtext laden",
3699
+ "showBuiltin": "Mit Standard vergleichen",
3700
+ "hideBuiltin": "Standard ausblenden",
3701
+ "restoringFrom": "Aus Version {n} geladen. Speichern, um sie wieder aktiv zu setzen.",
3702
+ "builtinHeading": "Standardprompt",
3703
+ "historyHeading": "Versionsverlauf",
3704
+ "historyEmpty": "Dieser Agent wurde in diesem Arbeitsbereich noch nie bearbeitet.",
3705
+ "live": "Aktiv",
3706
+ "restore": "Laden",
3707
+ "restoreTooltip": "Diese Version in den Editor laden",
3708
+ "revision": {
3709
+ "edit": "Version {n} (bearbeitet)",
3710
+ "restored": "Version {n} (aus Version {from} wiederhergestellt)",
3711
+ "builtin": "Version {n} (zurück zum Standard)"
3712
+ },
3713
+ "toast": {
3714
+ "saved": "Prompt gespeichert",
3715
+ "reverted": "Zurück zum Standardprompt",
3716
+ "saveFailed": "Prompt konnte nicht gespeichert werden",
3717
+ "loadFailed": "Prompt konnte nicht geladen werden",
3718
+ "conflict": "Jemand anderes hat diesen Prompt geändert. Neu geladen - wenden Sie Ihre Änderung erneut an."
3719
+ }
3720
+ },
3681
3721
  "requirements": {
3682
3722
  "title": "Anforderungsprüfung",
3683
3723
  "iteration": "Iteration {current} / {max}",
@@ -4520,7 +4560,8 @@
4520
4560
  "env_test_infraless": "Nichts zu testen",
4521
4561
  "env_test_not_provisionable": "Umgebungs-Handler nicht konfiguriert",
4522
4562
  "env_test_no_vcs": "Git-Anbieter nicht verbunden",
4523
- "env_test_connection_failed": "Umgebungsverbindung fehlgeschlagen"
4563
+ "env_test_connection_failed": "Umgebungsverbindung fehlgeschlagen",
4564
+ "prompt_revision_conflict": "Prompt von jemand anderem geändert"
4524
4565
  },
4525
4566
  "description": {
4526
4567
  "dependencies_unmet": "Diese Aufgabe hängt von anderen ab, die noch nicht abgeschlossen sind. Schließe sie ab oder gib sie frei und starte dann erneut.",
@@ -4544,7 +4585,8 @@
4544
4585
  "env_test_not_provisionable_type_mismatch": "Mehr als ein Umgebungs-Handler könnte auf den Bereitstellungstyp dieses Service passen. Lege für diesen Service eine manifest id fest, sodass genau einer aufgelöst wird, oder entferne den überlappenden Handler in Infrastruktur → Testumgebungen.",
4545
4586
  "env_test_no_vcs": "Der Selbsttest benötigt einen Git-Anbieter, um seinen Wegwerf-Branch zu erstellen und zu löschen, aber dieser Workspace ist mit keinem verbunden.",
4546
4587
  "env_test_connection_failed": "Der Umgebungs-Handler dieses Dienstes hat seinen Verbindungstest nicht bestanden. Prüfen Sie Endpunkt, Anmeldedaten und Projekteinstellungen und testen Sie die Verbindung erneut.",
4547
- "env_test_connection_failed_detail": "Der Umgebungs-Handler dieses Dienstes hat seinen Verbindungstest nicht bestanden: {detail}. Prüfen Sie Endpunkt, Anmeldedaten und Projekteinstellungen und testen Sie die Verbindung erneut."
4588
+ "env_test_connection_failed_detail": "Der Umgebungs-Handler dieses Dienstes hat seinen Verbindungstest nicht bestanden: {detail}. Prüfen Sie Endpunkt, Anmeldedaten und Projekteinstellungen und testen Sie die Verbindung erneut.",
4589
+ "prompt_revision_conflict": "Eine andere Änderung an diesem Prompt war zuerst da. Laden Sie ihn neu und wenden Sie Ihre Änderung darauf erneut an."
4548
4590
  },
4549
4591
  "action": {
4550
4592
  "connectGitHub": "GitHub verbinden",
@@ -5283,7 +5325,12 @@
5283
5325
  "forkOf": "Fork · {name}",
5284
5326
  "newVersionOf": "Neue Version von · {name}",
5285
5327
  "saveVersion": "Neue Version speichern",
5286
- "hint": "Wähle einen Prompt, um eine ausgelieferte Baseline zu forken oder einen Kandidaten zu versionieren. Jedes Speichern hängt eine unveränderliche Version an, die du testen kannst."
5328
+ "hint": "Wähle einen Prompt, um eine ausgelieferte Baseline zu forken oder einen Kandidaten zu versionieren. Jedes Speichern hängt eine unveränderliche Version an, die du testen kannst.",
5329
+ "liveInWorkspace": "Aktiv",
5330
+ "fromWorkspace": "Ihr Arbeitsbereich",
5331
+ "promoteTitle": "Diesen Prompt für diesen Agenten aktiv schalten",
5332
+ "promoted": "Jetzt der aktive Prompt für {agent}",
5333
+ "promoteFailed": "Prompt konnte nicht aktiviert werden"
5287
5334
  },
5288
5335
  "fixtureKind": {
5289
5336
  "requirements": "Anforderungen",
@@ -556,7 +556,8 @@
556
556
  "env_test_infraless": "Nothing to test",
557
557
  "env_test_not_provisionable": "Environment handler not configured",
558
558
  "env_test_no_vcs": "Git provider not connected",
559
- "env_test_connection_failed": "Environment connection failed"
559
+ "env_test_connection_failed": "Environment connection failed",
560
+ "prompt_revision_conflict": "Prompt changed by someone else"
560
561
  },
561
562
  "description": {
562
563
  "dependencies_unmet": "This task depends on others that aren't finished yet. Complete or unblock them, then start it again.",
@@ -583,7 +584,8 @@
583
584
  "env_test_connection_failed_detail": "The environment handler for this service failed its connection test: {detail}. Check its endpoint, credentials and project settings, then re-test the connection.",
584
585
  "@env_test_connection_failed_detail": {
585
586
  "description": "Keep the named placeholder for the failure detail intact (the environment provider's connection-test error message, injected at runtime)."
586
- }
587
+ },
588
+ "prompt_revision_conflict": "Another edit to this prompt landed first. Reload it and re-apply your change on top."
587
589
  },
588
590
  "action": {
589
591
  "connectGitHub": "Connect GitHub",
@@ -1153,6 +1155,9 @@
1153
1155
  "validationChecks": {
1154
1156
  "title": "Pre-PR validation",
1155
1157
  "sectionHint": "Commands run against the checkout after the coder finishes and before a pull request is opened. A failure is handed back to the agent to fix; only a passing checkout opens a PR.",
1158
+ "dependencyInstall": "Dependency install",
1159
+ "@dependencyInstall": "Field label for a shell command. \"install\" is a NOUN here (the installation step), not the imperative verb — it labels the input holding a command such as `pnpm install`, it does not ask the user to install anything.",
1160
+ "dependencyInstallHint": "Runs before the agent starts",
1156
1161
  "hint": "Each command runs with `sh -c` in this service's checkout, in order. Fix the code, not the check — the agent is told not to weaken them.",
1157
1162
  "clear": "Clear",
1158
1163
  "configNoun": "validation checks",
@@ -1172,6 +1177,7 @@
1172
1177
  "action": "Detect",
1173
1178
  "hint": "Suggest checks from this service's repository",
1174
1179
  "added": "Added {count} check | Added {count} checks",
1180
+ "installOnly": "Filled in the dependency install",
1175
1181
  "found": "Recognised {ecosystems}.",
1176
1182
  "capped": "Some suggestions were left out — a service takes at most {max} checks.",
1177
1183
  "nothingNew": "Nothing to add",
@@ -4032,7 +4038,9 @@
4032
4038
  "skillNoneAvailable": "No skills available. Link a skill source in account settings.",
4033
4039
  "skillMissing": "This skill is no longer in the catalog; pick another.",
4034
4040
  "skillNeedsPick": "A Skill step needs a skill selected before you can save.",
4035
- "purposeStepsConflict": "This purpose writes no code and runs no tests, but the pipeline still has implementation or testing steps. Remove them or switch the purpose to Build."
4041
+ "purposeStepsConflict": "This purpose writes no code and runs no tests, but the pipeline still has implementation or testing steps. Remove them or switch the purpose to Build.",
4042
+ "promptEditTooltip": "Edit this agent's system prompt",
4043
+ "promptEditedTooltip": "This agent's system prompt is edited for this workspace"
4036
4044
  },
4037
4045
  "progress": {
4038
4046
  "status": {
@@ -4106,6 +4114,50 @@
4106
4114
  }
4107
4115
  }
4108
4116
  },
4117
+ "agentPrompt": {
4118
+ "title": "System prompt: {agent}",
4119
+ "description": "Replace what this agent is told to do, for every run in this workspace.",
4120
+ "customized": "Edited",
4121
+ "usingBuiltin": "Built-in",
4122
+ "@usingBuiltin": {
4123
+ "description": "Badge meaning this agent runs the prompt the product ships. \"Built-in\" refers to that shipped prompt specifically, not to a user-set default."
4124
+ },
4125
+ "managedNotice": "The platform appends its own non-editable rules to whatever you save here.",
4126
+ "showAppended": "Show what gets appended",
4127
+ "hideAppended": "Hide what gets appended",
4128
+ "appendedHeading": "Appended by the platform (not editable)",
4129
+ "placeholder": "Describe what this agent should do.",
4130
+ "save": "Save as new version",
4131
+ "revert": "Back to built-in",
4132
+ "loadBuiltin": "Load built-in text",
4133
+ "showBuiltin": "Compare with built-in",
4134
+ "hideBuiltin": "Hide built-in",
4135
+ "restoringFrom": "Loaded from version {n}. Save to make it live again.",
4136
+ "builtinHeading": "Built-in prompt",
4137
+ "historyHeading": "Version history",
4138
+ "historyEmpty": "This agent has never been edited in this workspace.",
4139
+ "live": "Live",
4140
+ "@live": {
4141
+ "description": "Adjective, not a verb: labels the revision that is currently in effect."
4142
+ },
4143
+ "restore": "Load",
4144
+ "@restore": {
4145
+ "description": "Verb, imperative: loads the chosen revision into the editor. It does NOT save it - the user still presses Save."
4146
+ },
4147
+ "restoreTooltip": "Load this version into the editor",
4148
+ "revision": {
4149
+ "edit": "Version {n} (edited)",
4150
+ "restored": "Version {n} (restored from version {from})",
4151
+ "builtin": "Version {n} (back to built-in)"
4152
+ },
4153
+ "toast": {
4154
+ "saved": "Prompt saved",
4155
+ "reverted": "Back to the built-in prompt",
4156
+ "saveFailed": "Couldn't save the prompt",
4157
+ "loadFailed": "Couldn't load the prompt",
4158
+ "conflict": "Someone else changed this prompt. Reloaded - re-apply your edit."
4159
+ }
4160
+ },
4109
4161
  "riskPolicy": {
4110
4162
  "health": {
4111
4163
  "title": "Risk policy updates",
@@ -5239,7 +5291,18 @@
5239
5291
  "forkOf": "Fork · {name}",
5240
5292
  "newVersionOf": "New version of · {name}",
5241
5293
  "saveVersion": "Save new version",
5242
- "hint": "Pick a prompt to fork a shipped baseline or version a candidate. Each save appends an immutable version you can put under test."
5294
+ "hint": "Pick a prompt to fork a shipped baseline or version a candidate. Each save appends an immutable version you can put under test.",
5295
+ "liveInWorkspace": "Live",
5296
+ "@liveInWorkspace": {
5297
+ "description": "Adjective, not a verb: badges the prompt version this workspace is currently running."
5298
+ },
5299
+ "fromWorkspace": "Your workspace",
5300
+ "@fromWorkspace": {
5301
+ "description": "Badge on a prompt version that came from this workspace's own edits, as opposed to one the product ships."
5302
+ },
5303
+ "promoteTitle": "Make this the live prompt for this agent",
5304
+ "promoted": "Now the live prompt for {agent}",
5305
+ "promoteFailed": "Couldn't promote this prompt"
5243
5306
  },
5244
5307
  "fixtureKind": {
5245
5308
  "requirements": "requirements",