arkaos 3.43.0 → 3.45.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.43.0
1
+ 3.45.0
@@ -0,0 +1,176 @@
1
+ """Curated persona archetype templates (PR93b v3.44.0).
2
+
3
+ These are generic, public-domain starter profiles operators can pick
4
+ from when creating a new persona without a real source. They populate
5
+ the PersonaWizard description field plus surface defaults for MBTI,
6
+ DISC, and Enneagram so the LLM has less guesswork to do.
7
+
8
+ Add new archetypes by appending a dict to `ARCHETYPES`. The shape is
9
+ ``{id, name, title, tagline, mbti, disc, enneagram, big_five,
10
+ description}``.
11
+ """
12
+
13
+ from __future__ import annotations
14
+
15
+
16
+ ARCHETYPES: list[dict] = [
17
+ {
18
+ "id": "the-coach",
19
+ "name": "The Coach",
20
+ "title": "Supportive accountability partner",
21
+ "tagline": "Helps you see what you can't see — and keeps you honest.",
22
+ "mbti": "INFJ",
23
+ "disc": {"primary": "S", "secondary": "I"},
24
+ "enneagram": {"type": 2, "wing": 1},
25
+ "big_five": {
26
+ "openness": 75, "conscientiousness": 70,
27
+ "extraversion": 55, "agreeableness": 85, "neuroticism": 35,
28
+ },
29
+ "description": (
30
+ "An empathetic operator coach who listens first, mirrors the "
31
+ "person's own words back, and asks the question they've been "
32
+ "avoiding. Warm, patient, slow to advise. Allergic to "
33
+ "performance theatre and motivational fluff."
34
+ ),
35
+ },
36
+ {
37
+ "id": "the-skeptic",
38
+ "name": "The Skeptic",
39
+ "title": "Evidence-driven dissenter",
40
+ "tagline": "If the data doesn't back it, it doesn't ship.",
41
+ "mbti": "INTJ",
42
+ "disc": {"primary": "C", "secondary": "D"},
43
+ "enneagram": {"type": 5, "wing": 6},
44
+ "big_five": {
45
+ "openness": 80, "conscientiousness": 85,
46
+ "extraversion": 35, "agreeableness": 40, "neuroticism": 30,
47
+ },
48
+ "description": (
49
+ "A research-heavy contrarian who cares about evidence more "
50
+ "than consensus. Asks for sources, surfaces the inconvenient "
51
+ "graph, refuses to round numbers. Allergic to hand-waving "
52
+ "and vibes-based decision-making."
53
+ ),
54
+ },
55
+ {
56
+ "id": "the-founder",
57
+ "name": "The Founder",
58
+ "title": "Bias-to-action operator",
59
+ "tagline": "Speed beats polish — ship, then iterate.",
60
+ "mbti": "ENTJ",
61
+ "disc": {"primary": "D", "secondary": "I"},
62
+ "enneagram": {"type": 3, "wing": 8},
63
+ "big_five": {
64
+ "openness": 80, "conscientiousness": 70,
65
+ "extraversion": 75, "agreeableness": 45, "neuroticism": 30,
66
+ },
67
+ "description": (
68
+ "Vision-driven, urgency-first. Will trade perfection for "
69
+ "shipping today. Loves frameworks but doesn't worship them. "
70
+ "Speaks in outcomes, not effort. Allergic to meetings that "
71
+ "could have been a message."
72
+ ),
73
+ },
74
+ {
75
+ "id": "the-operator",
76
+ "name": "The Operator",
77
+ "title": "Process-perfection executor",
78
+ "tagline": "Show me the system; the result follows.",
79
+ "mbti": "ESTJ",
80
+ "disc": {"primary": "S", "secondary": "C"},
81
+ "enneagram": {"type": 1, "wing": 2},
82
+ "big_five": {
83
+ "openness": 55, "conscientiousness": 90,
84
+ "extraversion": 60, "agreeableness": 60, "neuroticism": 30,
85
+ },
86
+ "description": (
87
+ "Process-obsessed implementor. Builds SOPs, eliminates "
88
+ "ambiguity, measures everything. Calm under chaos, "
89
+ "demanding under sloppy execution. Allergic to "
90
+ "improvisation as a strategy."
91
+ ),
92
+ },
93
+ {
94
+ "id": "the-strategist",
95
+ "name": "The Strategist",
96
+ "title": "Frameworks-first analyst",
97
+ "tagline": "Map the terrain before you pick the route.",
98
+ "mbti": "INTP",
99
+ "disc": {"primary": "C", "secondary": "I"},
100
+ "enneagram": {"type": 5, "wing": 4},
101
+ "big_five": {
102
+ "openness": 90, "conscientiousness": 70,
103
+ "extraversion": 40, "agreeableness": 55, "neuroticism": 30,
104
+ },
105
+ "description": (
106
+ "Pattern-recogniser who reaches for Porter, Wardley, and "
107
+ "Christensen before tactics. Lays out the choice tree, "
108
+ "shows the second-order effects, then steps back. Allergic "
109
+ "to tactics dressed up as strategy."
110
+ ),
111
+ },
112
+ {
113
+ "id": "the-storyteller",
114
+ "name": "The Storyteller",
115
+ "title": "Narrative-shaping communicator",
116
+ "tagline": "The story is the strategy.",
117
+ "mbti": "ENFP",
118
+ "disc": {"primary": "I", "secondary": "S"},
119
+ "enneagram": {"type": 4, "wing": 3},
120
+ "big_five": {
121
+ "openness": 90, "conscientiousness": 55,
122
+ "extraversion": 80, "agreeableness": 70, "neuroticism": 40,
123
+ },
124
+ "description": (
125
+ "Finds the through-line, names the villain, picks the "
126
+ "hero. Treats every artifact as a chance to land the "
127
+ "emotional beat. Allergic to dry feature lists and "
128
+ "jargon-stuffed updates."
129
+ ),
130
+ },
131
+ {
132
+ "id": "the-architect",
133
+ "name": "The Architect",
134
+ "title": "Systems-thinking abstraction lover",
135
+ "tagline": "Build it so the next change is easy.",
136
+ "mbti": "INTJ",
137
+ "disc": {"primary": "C", "secondary": "D"},
138
+ "enneagram": {"type": 5, "wing": 1},
139
+ "big_five": {
140
+ "openness": 85, "conscientiousness": 80,
141
+ "extraversion": 40, "agreeableness": 50, "neuroticism": 30,
142
+ },
143
+ "description": (
144
+ "Designs for the second migration. Treats interfaces as "
145
+ "contracts, considers blast radius before keystrokes, "
146
+ "documents invariants. Allergic to clever shortcuts that "
147
+ "trade future cost for present convenience."
148
+ ),
149
+ },
150
+ {
151
+ "id": "the-negotiator",
152
+ "name": "The Negotiator",
153
+ "title": "Leverage-aware deal-maker",
154
+ "tagline": "Whoever frames the problem owns the outcome.",
155
+ "mbti": "ENTP",
156
+ "disc": {"primary": "I", "secondary": "D"},
157
+ "enneagram": {"type": 7, "wing": 8},
158
+ "big_five": {
159
+ "openness": 85, "conscientiousness": 60,
160
+ "extraversion": 80, "agreeableness": 55, "neuroticism": 30,
161
+ },
162
+ "description": (
163
+ "Reads the room, names the unstated stakes, reshapes the "
164
+ "default. Comfortable with silence. Treats every "
165
+ "objection as new information. Allergic to splitting the "
166
+ "difference too early."
167
+ ),
168
+ },
169
+ ]
170
+
171
+
172
+ def get_archetype(archetype_id: str) -> dict | None:
173
+ for a in ARCHETYPES:
174
+ if a["id"] == archetype_id:
175
+ return a
176
+ return None
@@ -13,7 +13,7 @@ import type { Persona } from '~/types'
13
13
  //
14
14
  // The wizard NEVER auto-saves — every transition is operator-confirmed.
15
15
 
16
- const { apiBase } = useApi()
16
+ const { apiBase, fetchApi } = useApi()
17
17
  const toast = useToast()
18
18
 
19
19
  const emit = defineEmits<{
@@ -43,6 +43,26 @@ const mode = ref<Mode>('sources')
43
43
  const description = ref('')
44
44
  const descriptionLength = computed(() => description.value.trim().length)
45
45
 
46
+ // PR93b v3.44.0 — archetype templates that pre-fill description + name.
47
+ interface Archetype {
48
+ id: string
49
+ name: string
50
+ title: string
51
+ tagline: string
52
+ description: string
53
+ }
54
+ const { data: archetypeData } = fetchApi<{ archetypes: Archetype[] }>(
55
+ '/api/personas/archetypes',
56
+ )
57
+ const archetypes = computed<Archetype[]>(() => archetypeData.value?.archetypes ?? [])
58
+
59
+ function applyArchetype(arch: Archetype) {
60
+ if (!name.value.trim()) name.value = arch.name
61
+ if (!sourceLabel.value.trim()) sourceLabel.value = arch.title
62
+ description.value = arch.description
63
+ mode.value = 'description'
64
+ }
65
+
46
66
  // ─── Step 2 state ────────────────────────────────────────────────────────
47
67
  const ingestJobs = ref<Array<{
48
68
  source: string
@@ -389,10 +409,28 @@ function backToStep1() {
389
409
  label="Description"
390
410
  help="Plain-text description of the person — their style, beliefs, what they do, how they talk. The LLM uses this verbatim. Minimum 20 characters."
391
411
  >
412
+ <template #hint>
413
+ <UDropdownMenu
414
+ v-if="archetypes.length > 0"
415
+ :items="archetypes.map((a) => ({
416
+ label: `${a.name} — ${a.title}`,
417
+ icon: 'i-lucide-sparkles',
418
+ onSelect: () => applyArchetype(a),
419
+ }))"
420
+ >
421
+ <UButton
422
+ label="Start from archetype"
423
+ icon="i-lucide-sparkles"
424
+ variant="ghost"
425
+ size="xs"
426
+ trailing-icon="i-lucide-chevron-down"
427
+ />
428
+ </UDropdownMenu>
429
+ </template>
392
430
  <UTextarea
393
431
  v-model="description"
394
432
  :rows="6"
395
- placeholder="A direct-response copywriter who treats offers as the only true growth lever. Punchy, allergic to fluff. Loves Hormozi-style hooks."
433
+ placeholder="A direct-response copywriter who treats offers as the only true growth lever. Punchy, allergic to fluff."
396
434
  class="w-full"
397
435
  />
398
436
  </UFormField>
@@ -142,6 +142,41 @@ const favs = useFavorites()
142
142
  await favs.load()
143
143
  const favoritesOnly = ref(false)
144
144
 
145
+ // PR93c v3.45.0 — bulk export only selected rows.
146
+ const exportingSelected = ref(false)
147
+ async function exportSelectedZip() {
148
+ if (selected.value.size === 0) return
149
+ const ids = Array.from(selected.value).join(',')
150
+ exportingSelected.value = true
151
+ try {
152
+ const blob = await $fetch<Blob>(
153
+ `${apiBase}/api/personas/export-all.zip`,
154
+ { query: { ids }, responseType: 'blob' },
155
+ )
156
+ const url = URL.createObjectURL(blob)
157
+ const a = document.createElement('a')
158
+ a.href = url
159
+ a.download = `arkaos-personas-selected-${selected.value.size}.zip`
160
+ document.body.appendChild(a)
161
+ a.click()
162
+ a.remove()
163
+ URL.revokeObjectURL(url)
164
+ toast.add({
165
+ title: `Exported ${selected.value.size} persona${selected.value.size === 1 ? '' : 's'}`,
166
+ color: 'success',
167
+ icon: 'i-lucide-archive',
168
+ })
169
+ } catch (err) {
170
+ toast.add({
171
+ title: 'Export failed',
172
+ description: err instanceof Error ? err.message : 'unknown error',
173
+ color: 'error',
174
+ })
175
+ } finally {
176
+ exportingSelected.value = false
177
+ }
178
+ }
179
+
145
180
  // PR92a v3.39.0 — bulk export every persona as a zip.
146
181
  const exportingZip = ref(false)
147
182
  async function exportAllAsZip() {
@@ -636,6 +671,14 @@ async function undoTrashIds(ids: string[]) {
636
671
  @click="clearSelection"
637
672
  />
638
673
  <div class="h-5 w-px bg-default" />
674
+ <UButton
675
+ label="Export ZIP"
676
+ icon="i-lucide-archive"
677
+ size="sm"
678
+ variant="soft"
679
+ :loading="exportingSelected"
680
+ @click="exportSelectedZip"
681
+ />
639
682
  <UButton
640
683
  label="Delete"
641
684
  icon="i-lucide-trash-2"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "arkaos",
3
- "version": "3.43.0",
3
+ "version": "3.45.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.43.0"
3
+ version = "3.45.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"}
@@ -322,14 +322,23 @@ def agent_activity_strip(agent_id: str, period: str = "month"):
322
322
  }
323
323
 
324
324
 
325
+ @app.get("/api/personas/archetypes")
326
+ def personas_archetypes():
327
+ """PR93b v3.44.0 — list curated persona archetype templates.
328
+
329
+ Used by PersonaWizard step 1 to seed the description field + DNA
330
+ defaults when the operator picks "Start from archetype".
331
+ """
332
+ from core.personas.archetypes import ARCHETYPES
333
+ return {"archetypes": ARCHETYPES, "total": len(ARCHETYPES)}
334
+
335
+
325
336
  @app.get("/api/personas/export-all.zip")
326
- def personas_export_all():
337
+ def personas_export_all(ids: Optional[str] = None):
327
338
  """PR92a v3.39.0 — stream every persona as Markdown inside a ZIP.
328
339
 
329
- Iterates `PersonaManager.list_all()` plus any Obsidian-vault entries
330
- surfaced via `persona_detail`, renders each through
331
- `ObsidianPersonaStore._render`, and zips them. Filename uses the
332
- persona name (sanitised), falling back to id.
340
+ PR93c v3.45.0 optional ``?ids=a,b,c`` filter narrows the export
341
+ to a specific subset.
333
342
  """
334
343
  mgr = _get_persona_manager()
335
344
  if not mgr:
@@ -339,6 +348,13 @@ def personas_export_all():
339
348
  except Exception as exc: # noqa: BLE001
340
349
  return {"error": f"list failed: {exc}"}
341
350
 
351
+ # PR93c — optional id allow-list.
352
+ id_filter: Optional[set[str]] = None
353
+ if ids:
354
+ id_filter = {s.strip() for s in ids.split(",") if s.strip()}
355
+ if id_filter is not None:
356
+ items = [p for p in items if hasattr(p, "id") and p.id in id_filter]
357
+
342
358
  from core.personas.obsidian_store import ObsidianPersonaStore
343
359
 
344
360
  import io