arkaos 3.51.0 → 3.52.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/VERSION CHANGED
@@ -1 +1 @@
1
- 3.51.0
1
+ 3.52.0
@@ -105,6 +105,58 @@ function markedHtml(src: string): string {
105
105
  }
106
106
  }
107
107
 
108
+ // PR95b v3.52.0 — edit YAML inline (modal).
109
+ const yamlEditorOpen = ref(false)
110
+ const yamlEditorDraft = ref('')
111
+ const yamlEditorSaving = ref(false)
112
+
113
+ async function openYamlEditor() {
114
+ if (!agent.value) return
115
+ // Fetch raw YAML once when opening (already on backend via PR89d).
116
+ try {
117
+ const blob = await $fetch<Blob>(
118
+ `${apiBase}/api/agents/${agentId}/yaml`,
119
+ { responseType: 'blob' },
120
+ )
121
+ yamlEditorDraft.value = await blob.text()
122
+ yamlEditorOpen.value = true
123
+ } catch (err) {
124
+ toast.add({
125
+ title: 'Failed to load YAML',
126
+ description: err instanceof Error ? err.message : 'unknown error',
127
+ color: 'error',
128
+ })
129
+ }
130
+ }
131
+
132
+ async function saveYamlEditor() {
133
+ if (!agent.value) return
134
+ yamlEditorSaving.value = true
135
+ try {
136
+ const res = await $fetch<{ updated?: boolean, error?: string }>(
137
+ `${apiBase}/api/agents/${agentId}/yaml`,
138
+ { method: 'PUT', body: { content: yamlEditorDraft.value } },
139
+ )
140
+ if (res.error) throw new Error(res.error)
141
+ toast.add({
142
+ title: 'YAML updated',
143
+ description: agentId,
144
+ color: 'success',
145
+ icon: 'i-lucide-check',
146
+ })
147
+ yamlEditorOpen.value = false
148
+ await refresh()
149
+ } catch (err) {
150
+ toast.add({
151
+ title: 'Save failed',
152
+ description: err instanceof Error ? err.message : 'unknown error',
153
+ color: 'error',
154
+ })
155
+ } finally {
156
+ yamlEditorSaving.value = false
157
+ }
158
+ }
159
+
108
160
  // PR89d v3.30.0 — download YAML.
109
161
  const downloadingYaml = ref(false)
110
162
  async function downloadYaml() {
@@ -402,6 +454,13 @@ function formatTokens(n: number): string {
402
454
  :loading="downloadingYaml"
403
455
  @click="downloadYaml"
404
456
  />
457
+ <UButton
458
+ label="Edit YAML"
459
+ icon="i-lucide-file-code"
460
+ variant="ghost"
461
+ size="sm"
462
+ @click="openYamlEditor"
463
+ />
405
464
  <UButton
406
465
  label="Edit"
407
466
  icon="i-lucide-pencil"
@@ -552,6 +611,57 @@ function formatTokens(n: number): string {
552
611
  </ol>
553
612
  </section>
554
613
 
614
+ <!-- PR95b v3.52.0 — YAML editor modal -->
615
+ <UModal v-model:open="yamlEditorOpen" :ui="{ content: 'max-w-3xl' }">
616
+ <template #content>
617
+ <UCard>
618
+ <template #header>
619
+ <div class="flex items-center justify-between gap-3">
620
+ <div>
621
+ <h2 class="text-lg font-bold">Edit agent YAML</h2>
622
+ <p class="text-xs text-muted mt-0.5 font-mono">{{ agent?.id }}</p>
623
+ </div>
624
+ <UButton
625
+ icon="i-lucide-x"
626
+ variant="ghost"
627
+ size="sm"
628
+ :disabled="yamlEditorSaving"
629
+ @click="yamlEditorOpen = false"
630
+ />
631
+ </div>
632
+ </template>
633
+ <UTextarea
634
+ v-model="yamlEditorDraft"
635
+ :rows="20"
636
+ class="w-full font-mono text-xs"
637
+ />
638
+ <template #footer>
639
+ <div class="flex items-center justify-between gap-2 text-xs">
640
+ <p class="text-muted">
641
+ Edits go through the same validator as PUT /api/agents/{id}/yaml —
642
+ parse + dict root + matching id. Tier 0 agents are locked.
643
+ </p>
644
+ <div class="flex gap-2 shrink-0">
645
+ <UButton
646
+ label="Cancel"
647
+ variant="ghost"
648
+ :disabled="yamlEditorSaving"
649
+ @click="yamlEditorOpen = false"
650
+ />
651
+ <UButton
652
+ label="Save"
653
+ icon="i-lucide-check"
654
+ color="primary"
655
+ :loading="yamlEditorSaving"
656
+ @click="saveYamlEditor"
657
+ />
658
+ </div>
659
+ </div>
660
+ </template>
661
+ </UCard>
662
+ </template>
663
+ </UModal>
664
+
555
665
  <AgentEditDrawer
556
666
  v-model="editOpen"
557
667
  :agent="agent"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "arkaos",
3
- "version": "3.51.0",
3
+ "version": "3.52.0",
4
4
  "description": "The Operating System for AI Agent Teams",
5
5
  "type": "module",
6
6
  "bin": {
package/pyproject.toml CHANGED
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "arkaos-core"
3
- version = "3.51.0"
3
+ version = "3.52.0"
4
4
  description = "Core engine for ArkaOS — The Operating System for AI Agent Teams"
5
5
  readme = "README.md"
6
6
  license = {text = "MIT"}
@@ -449,6 +449,51 @@ def persona_download_markdown(persona_id: str):
449
449
  )
450
450
 
451
451
 
452
+ @app.put("/api/agents/{agent_id}/yaml")
453
+ def agent_update_yaml(agent_id: str, body: dict):
454
+ """PR95b v3.52.0 — overwrite an agent's YAML file in place.
455
+
456
+ Body: ``{"content": "<full YAML>"}``. Mirrors the workflow YAML
457
+ editor (PR94d): parses the content, requires a dict root + an `id`
458
+ that matches the URL param. Atomic write via tmp + replace.
459
+
460
+ Refuses to mutate Tier 0 agents — those are governance fixtures.
461
+ """
462
+ if not isinstance(body, dict):
463
+ return {"error": "body must be an object"}
464
+ content = str(body.get("content") or "")
465
+ if not content.strip():
466
+ return {"error": "content is required"}
467
+ yaml_file = _resolve_agent_yaml(agent_id)
468
+ if yaml_file is None:
469
+ return {"error": "Agent not found"}
470
+ if _agent_tier_from_yaml(yaml_file) == 0:
471
+ return {"error": "Cannot edit Tier 0 (C-Suite) YAML via this endpoint"}
472
+ try:
473
+ import yaml as _yaml
474
+ parsed = _yaml.safe_load(content)
475
+ except Exception as exc: # noqa: BLE001
476
+ return {"error": f"YAML parse failed: {exc}"}
477
+ if not isinstance(parsed, dict):
478
+ return {"error": "YAML root must be a mapping"}
479
+ if not parsed.get("id"):
480
+ return {"error": "YAML must include a non-empty 'id' field"}
481
+ if parsed.get("id") != agent_id:
482
+ return {
483
+ "error": (
484
+ "YAML 'id' must match the URL param "
485
+ f"({parsed.get('id')!r} vs {agent_id!r})"
486
+ ),
487
+ }
488
+ try:
489
+ tmp = yaml_file.with_suffix(yaml_file.suffix + ".tmp")
490
+ tmp.write_text(content, encoding="utf-8")
491
+ tmp.replace(yaml_file)
492
+ except OSError as exc:
493
+ return {"error": f"write failed: {exc}"}
494
+ return {"updated": True, "id": agent_id, "yaml_path": str(yaml_file)}
495
+
496
+
452
497
  @app.get("/api/agents/{agent_id}/yaml")
453
498
  def agent_download_yaml(agent_id: str):
454
499
  """PR89d v3.30.0 — return the raw YAML for the agent.