arkaos 3.6.1 → 3.8.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.
|
|
1
|
+
3.8.0
|
|
@@ -115,6 +115,81 @@ function csvToList(value: string): string[] {
|
|
|
115
115
|
type SuggestField = 'mental_models_primary' | 'frameworks' | 'expertise_domains' | 'communication_avoid'
|
|
116
116
|
const suggestingField = ref<SuggestField | null>(null)
|
|
117
117
|
|
|
118
|
+
// PR84a v3.7.0 — AI Rewrite from description.
|
|
119
|
+
const rewriteOpen = ref(false)
|
|
120
|
+
const rewriteDescription = ref('')
|
|
121
|
+
const rewriting = ref(false)
|
|
122
|
+
|
|
123
|
+
async function rewriteFromDescription() {
|
|
124
|
+
if (!draft.value || !props.agent) return
|
|
125
|
+
const desc = rewriteDescription.value.trim()
|
|
126
|
+
if (desc.length < 20) {
|
|
127
|
+
toast.add({
|
|
128
|
+
title: 'Add more detail',
|
|
129
|
+
description: 'Describe the agent in at least a sentence or two.',
|
|
130
|
+
color: 'warning',
|
|
131
|
+
})
|
|
132
|
+
return
|
|
133
|
+
}
|
|
134
|
+
rewriting.value = true
|
|
135
|
+
try {
|
|
136
|
+
const res = await $fetch<{
|
|
137
|
+
draft: any
|
|
138
|
+
provider_name: string
|
|
139
|
+
error?: string
|
|
140
|
+
}>(`${apiBase}/api/agents/draft`, {
|
|
141
|
+
method: 'POST',
|
|
142
|
+
body: {
|
|
143
|
+
description: desc,
|
|
144
|
+
name: draft.value.name,
|
|
145
|
+
role: draft.value.role,
|
|
146
|
+
department: props.agent.department,
|
|
147
|
+
tier: draft.value.tier,
|
|
148
|
+
},
|
|
149
|
+
})
|
|
150
|
+
if (res.error) throw new Error(res.error)
|
|
151
|
+
applyRewrite(res.draft)
|
|
152
|
+
markDirty()
|
|
153
|
+
toast.add({
|
|
154
|
+
title: 'Rewritten',
|
|
155
|
+
description: `via ${res.provider_name} — review and Save when ready.`,
|
|
156
|
+
color: 'success',
|
|
157
|
+
icon: 'i-lucide-sparkles',
|
|
158
|
+
})
|
|
159
|
+
rewriteOpen.value = false
|
|
160
|
+
rewriteDescription.value = ''
|
|
161
|
+
} catch (err) {
|
|
162
|
+
toast.add({
|
|
163
|
+
title: 'Rewrite failed',
|
|
164
|
+
description: err instanceof Error ? err.message : 'unknown error',
|
|
165
|
+
color: 'error',
|
|
166
|
+
})
|
|
167
|
+
} finally {
|
|
168
|
+
rewriting.value = false
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
function applyRewrite(d: any) {
|
|
173
|
+
if (!draft.value) return
|
|
174
|
+
// NOTE: identity (id, department) stays. Behavioural DNA is intentionally
|
|
175
|
+
// not editable here, so we do not touch it. We rewrite the SAFE fields
|
|
176
|
+
// operators edit through this drawer.
|
|
177
|
+
const exp = d?.expertise ?? {}
|
|
178
|
+
if (Array.isArray(exp.domains)) draft.value.expertise_domains = exp.domains.map(String)
|
|
179
|
+
if (Array.isArray(exp.frameworks)) draft.value.frameworks = exp.frameworks.map(String)
|
|
180
|
+
if (exp.depth) draft.value.expertise_depth = String(exp.depth)
|
|
181
|
+
if (typeof exp.years_equivalent === 'number') draft.value.expertise_years = exp.years_equivalent
|
|
182
|
+
const mm = d?.mental_models ?? {}
|
|
183
|
+
if (Array.isArray(mm.primary)) draft.value.mental_models.primary = mm.primary.map(String)
|
|
184
|
+
if (Array.isArray(mm.secondary)) draft.value.mental_models.secondary = mm.secondary.map(String)
|
|
185
|
+
const comm = d?.communication ?? {}
|
|
186
|
+
if (comm.tone) draft.value.communication.tone = String(comm.tone)
|
|
187
|
+
if (comm.vocabulary_level) draft.value.communication.vocabulary_level = String(comm.vocabulary_level)
|
|
188
|
+
if (comm.preferred_format) draft.value.communication.preferred_format = String(comm.preferred_format)
|
|
189
|
+
if (comm.language) draft.value.communication.language = String(comm.language)
|
|
190
|
+
if (Array.isArray(comm.avoid)) draft.value.communication.avoid = comm.avoid.map(String)
|
|
191
|
+
}
|
|
192
|
+
|
|
118
193
|
// PR83c v3.5.0 — single-string suggester.
|
|
119
194
|
type StringField = 'tone' | 'preferred_format'
|
|
120
195
|
const suggestingString = ref<StringField | null>(null)
|
|
@@ -346,6 +421,52 @@ const vocabOptions = [
|
|
|
346
421
|
</template>
|
|
347
422
|
|
|
348
423
|
<div v-if="draft" class="space-y-6">
|
|
424
|
+
<!-- PR84a — AI Rewrite -->
|
|
425
|
+
<div class="rounded-xl border border-primary/30 bg-primary/5">
|
|
426
|
+
<button
|
|
427
|
+
type="button"
|
|
428
|
+
class="w-full flex items-center justify-between gap-3 p-3 text-left"
|
|
429
|
+
@click="rewriteOpen = !rewriteOpen"
|
|
430
|
+
>
|
|
431
|
+
<div class="flex items-center gap-2">
|
|
432
|
+
<UIcon name="i-lucide-sparkles" class="size-4 text-primary" />
|
|
433
|
+
<span class="text-sm font-semibold text-primary">Rewrite from description</span>
|
|
434
|
+
</div>
|
|
435
|
+
<UIcon
|
|
436
|
+
:name="rewriteOpen ? 'i-lucide-chevron-up' : 'i-lucide-chevron-down'"
|
|
437
|
+
class="size-4 text-muted"
|
|
438
|
+
/>
|
|
439
|
+
</button>
|
|
440
|
+
<div v-if="rewriteOpen" class="p-3 pt-0 space-y-3">
|
|
441
|
+
<p class="text-xs text-muted">
|
|
442
|
+
Paste a new description to regenerate expertise, mental models,
|
|
443
|
+
frameworks, and communication. Identity (name, role, department)
|
|
444
|
+
and behavioural DNA are preserved.
|
|
445
|
+
</p>
|
|
446
|
+
<UTextarea
|
|
447
|
+
v-model="rewriteDescription"
|
|
448
|
+
:rows="3"
|
|
449
|
+
placeholder="A senior strategist who decides fast and demands evidence. 10 years at McKinsey covering CPG..."
|
|
450
|
+
class="w-full"
|
|
451
|
+
/>
|
|
452
|
+
<div class="flex items-center justify-between">
|
|
453
|
+
<span class="text-xs text-muted">
|
|
454
|
+
{{ rewriteDescription.trim().length }} char{{ rewriteDescription.trim().length === 1 ? '' : 's' }}
|
|
455
|
+
· {{ rewriteDescription.trim().length >= 20 ? 'ready' : `${20 - rewriteDescription.trim().length} more needed` }}
|
|
456
|
+
</span>
|
|
457
|
+
<UButton
|
|
458
|
+
label="Rewrite"
|
|
459
|
+
icon="i-lucide-wand"
|
|
460
|
+
color="primary"
|
|
461
|
+
size="sm"
|
|
462
|
+
:loading="rewriting"
|
|
463
|
+
:disabled="rewriteDescription.trim().length < 20"
|
|
464
|
+
@click="rewriteFromDescription"
|
|
465
|
+
/>
|
|
466
|
+
</div>
|
|
467
|
+
</div>
|
|
468
|
+
</div>
|
|
469
|
+
|
|
349
470
|
<p class="rounded-lg border border-yellow-500/30 bg-yellow-500/5 p-3 text-xs text-muted">
|
|
350
471
|
<UIcon name="i-lucide-info" class="size-3.5 inline" />
|
|
351
472
|
Behavioural DNA (DISC, Enneagram, MBTI, Big Five) is locked here
|
|
@@ -158,9 +158,59 @@ function goToAgent(id: string) {
|
|
|
158
158
|
}
|
|
159
159
|
|
|
160
160
|
// PR83b v3.4.0 — bulk selection + delete.
|
|
161
|
+
// PR84b v3.8.0 — bulk move department.
|
|
161
162
|
const confirmDialog = useConfirmDialog()
|
|
162
163
|
const selected = ref<Set<string>>(new Set())
|
|
163
164
|
const bulkDeleting = ref(false)
|
|
165
|
+
const bulkMoving = ref(false)
|
|
166
|
+
|
|
167
|
+
const departmentMoveOptions = [
|
|
168
|
+
'dev', 'marketing', 'brand', 'finance', 'strategy', 'ecom', 'kb', 'ops',
|
|
169
|
+
'pm', 'saas', 'landing', 'content', 'community', 'sales', 'leadership', 'org',
|
|
170
|
+
].map((d) => ({
|
|
171
|
+
label: `Move to ${d}`,
|
|
172
|
+
icon: 'i-lucide-arrow-right',
|
|
173
|
+
onSelect: () => bulkMove(d),
|
|
174
|
+
}))
|
|
175
|
+
|
|
176
|
+
async function bulkMove(targetDept: string) {
|
|
177
|
+
if (selected.value.size === 0) return
|
|
178
|
+
const ids = Array.from(selected.value)
|
|
179
|
+
const ok = await confirmDialog({
|
|
180
|
+
title: `Move ${ids.length} agent${ids.length === 1 ? '' : 's'} to ${targetDept}?`,
|
|
181
|
+
description: 'The YAML files will be relocated and their `department:` field updated. Tier 0 agents and unknown departments are skipped.',
|
|
182
|
+
confirmLabel: `Move to ${targetDept}`,
|
|
183
|
+
cancelLabel: 'Cancel',
|
|
184
|
+
})
|
|
185
|
+
if (!ok) return
|
|
186
|
+
bulkMoving.value = true
|
|
187
|
+
const results = await Promise.allSettled(
|
|
188
|
+
ids.map((id) =>
|
|
189
|
+
$fetch<{ moved?: boolean, error?: string }>(`${apiBase}/api/agents/${id}/move`, {
|
|
190
|
+
method: 'POST',
|
|
191
|
+
body: { department: targetDept },
|
|
192
|
+
}),
|
|
193
|
+
),
|
|
194
|
+
)
|
|
195
|
+
const successes = results.filter(
|
|
196
|
+
(r) => r.status === 'fulfilled' && r.value.moved,
|
|
197
|
+
).length
|
|
198
|
+
const failures = ids.length - successes
|
|
199
|
+
toast.add({
|
|
200
|
+
title: successes > 0
|
|
201
|
+
? `Moved ${successes} agent${successes === 1 ? '' : 's'}`
|
|
202
|
+
: 'Nothing moved',
|
|
203
|
+
description: failures > 0
|
|
204
|
+
? `${failures} skipped (Tier 0, collision, or missing)`
|
|
205
|
+
: undefined,
|
|
206
|
+
color: successes > 0 && failures === 0
|
|
207
|
+
? 'success'
|
|
208
|
+
: failures > 0 && successes > 0 ? 'warning' : 'error',
|
|
209
|
+
})
|
|
210
|
+
clearSelection()
|
|
211
|
+
bulkMoving.value = false
|
|
212
|
+
await refreshAll()
|
|
213
|
+
}
|
|
164
214
|
|
|
165
215
|
function toggleSelected(id: string) {
|
|
166
216
|
if (selected.value.has(id)) selected.value.delete(id)
|
|
@@ -387,6 +437,16 @@ async function bulkDelete() {
|
|
|
387
437
|
@click="clearSelection"
|
|
388
438
|
/>
|
|
389
439
|
<div class="h-5 w-px bg-default" />
|
|
440
|
+
<UDropdownMenu :items="departmentMoveOptions">
|
|
441
|
+
<UButton
|
|
442
|
+
label="Move to..."
|
|
443
|
+
icon="i-lucide-folder-tree"
|
|
444
|
+
size="sm"
|
|
445
|
+
variant="soft"
|
|
446
|
+
:loading="bulkMoving"
|
|
447
|
+
trailing-icon="i-lucide-chevron-down"
|
|
448
|
+
/>
|
|
449
|
+
</UDropdownMenu>
|
|
390
450
|
<UButton
|
|
391
451
|
label="Delete"
|
|
392
452
|
icon="i-lucide-trash-2"
|
package/package.json
CHANGED
package/pyproject.toml
CHANGED
|
Binary file
|
package/scripts/dashboard-api.py
CHANGED
|
@@ -1178,6 +1178,56 @@ def persona_clone(persona_id: str, body: dict = {}):
|
|
|
1178
1178
|
return {"agent_id": agent_id, "department": department, "file": f"departments/{department}/agents/{agent_id}.yaml"}
|
|
1179
1179
|
|
|
1180
1180
|
|
|
1181
|
+
@app.post("/api/agents/{agent_id}/move")
|
|
1182
|
+
def agent_move(agent_id: str, body: dict):
|
|
1183
|
+
"""PR84b v3.8.0 — move an agent's YAML to another department.
|
|
1184
|
+
|
|
1185
|
+
Body: {"department": "<new-dept>"}
|
|
1186
|
+
Mutates the YAML's `department:` field AND moves the file across
|
|
1187
|
+
`departments/<src>/agents/` → `departments/<dst>/agents/`.
|
|
1188
|
+
|
|
1189
|
+
Refuses Tier 0 (C-Suite) like the delete endpoint. Refuses unknown
|
|
1190
|
+
target department. Refuses overwriting an existing file at the
|
|
1191
|
+
destination.
|
|
1192
|
+
"""
|
|
1193
|
+
if not isinstance(body, dict):
|
|
1194
|
+
return {"error": "body must be an object"}
|
|
1195
|
+
target_dept = (body.get("department") or "").strip().lower()
|
|
1196
|
+
if not target_dept:
|
|
1197
|
+
return {"error": "department is required"}
|
|
1198
|
+
yaml_file = _resolve_agent_yaml(agent_id)
|
|
1199
|
+
if yaml_file is None:
|
|
1200
|
+
return {"error": "Agent not found"}
|
|
1201
|
+
if _agent_tier_from_yaml(yaml_file) == 0:
|
|
1202
|
+
return {"error": "Cannot move Tier 0 (C-Suite) agents from the dashboard"}
|
|
1203
|
+
dest_dir = ARKAOS_ROOT / "departments" / target_dept / "agents"
|
|
1204
|
+
if not dest_dir.exists():
|
|
1205
|
+
return {"error": f"department '{target_dept}' not found"}
|
|
1206
|
+
dest_file = dest_dir / yaml_file.name
|
|
1207
|
+
if dest_file.exists():
|
|
1208
|
+
return {"error": f"target file already exists: {dest_file.name}"}
|
|
1209
|
+
try:
|
|
1210
|
+
if yaml_file.resolve() == dest_file.resolve():
|
|
1211
|
+
return {"moved": False, "id": agent_id, "yaml_path": str(yaml_file)}
|
|
1212
|
+
except FileNotFoundError:
|
|
1213
|
+
pass
|
|
1214
|
+
try:
|
|
1215
|
+
import yaml as _yaml
|
|
1216
|
+
raw = _yaml.safe_load(yaml_file.read_text(encoding="utf-8")) or {}
|
|
1217
|
+
if isinstance(raw, dict):
|
|
1218
|
+
raw["department"] = target_dept
|
|
1219
|
+
tmp = yaml_file.with_suffix(yaml_file.suffix + ".tmp")
|
|
1220
|
+
tmp.write_text(
|
|
1221
|
+
_yaml.safe_dump(raw, sort_keys=False, allow_unicode=True, default_flow_style=False),
|
|
1222
|
+
encoding="utf-8",
|
|
1223
|
+
)
|
|
1224
|
+
tmp.replace(yaml_file)
|
|
1225
|
+
yaml_file.rename(dest_file)
|
|
1226
|
+
except (OSError, ImportError) as exc:
|
|
1227
|
+
return {"error": f"move failed: {exc}"}
|
|
1228
|
+
return {"moved": True, "id": agent_id, "yaml_path": str(dest_file)}
|
|
1229
|
+
|
|
1230
|
+
|
|
1181
1231
|
@app.delete("/api/agents/{agent_id}")
|
|
1182
1232
|
def agent_delete(agent_id: str):
|
|
1183
1233
|
"""PR83b v3.4.0 — delete an agent's YAML file.
|