aimeat 1.34.0 → 1.35.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.
Files changed (58) hide show
  1. package/dist/locales/en.json +52 -4
  2. package/dist/locales/fi.json +52 -4
  3. package/dist/public/css/views/secretary.css +52 -0
  4. package/dist/public/js/i18n.js +9 -0
  5. package/dist/public/js/services/secretary-helpers.js +8 -4
  6. package/dist/public/js/services/secretary-policy.js +28 -3
  7. package/dist/public/views/profile/agents/agent-card.js +6 -4
  8. package/dist/public/views/profile/agents/tab-integration.js +5 -2
  9. package/dist/public/views/secretary/cards.js +46 -15
  10. package/dist/public/views/secretary/dashboard.js +68 -2
  11. package/dist/public/views/secretary.js +208 -40
  12. package/dist/src/cli/connect/mcp/tools/organisms.d.ts +2 -0
  13. package/dist/src/cli/connect/mcp/tools/organisms.d.ts.map +1 -1
  14. package/dist/src/cli/connect/mcp/tools/organisms.js +9 -1
  15. package/dist/src/cli/connect/mcp/tools/organisms.js.map +1 -1
  16. package/dist/src/cli/connect/onboarding-prompt.d.ts +3 -0
  17. package/dist/src/cli/connect/onboarding-prompt.d.ts.map +1 -1
  18. package/dist/src/cli/connect/onboarding-prompt.js +10 -3
  19. package/dist/src/cli/connect/onboarding-prompt.js.map +1 -1
  20. package/dist/src/generated/api-types.d.ts +309 -2
  21. package/dist/src/generated/api-types.d.ts.map +1 -1
  22. package/dist/src/mcp/agent-onboarding.d.ts +4 -0
  23. package/dist/src/mcp/agent-onboarding.d.ts.map +1 -1
  24. package/dist/src/mcp/agent-onboarding.js +28 -2
  25. package/dist/src/mcp/agent-onboarding.js.map +1 -1
  26. package/dist/src/models/agent-onboarding-schemas.d.ts +47 -0
  27. package/dist/src/models/agent-onboarding-schemas.d.ts.map +1 -1
  28. package/dist/src/models/agent-onboarding-schemas.js +117 -0
  29. package/dist/src/models/agent-onboarding-schemas.js.map +1 -1
  30. package/dist/src/routes/agent-onboarding.d.ts +5 -0
  31. package/dist/src/routes/agent-onboarding.d.ts.map +1 -1
  32. package/dist/src/routes/agent-onboarding.js +21 -2
  33. package/dist/src/routes/agent-onboarding.js.map +1 -1
  34. package/dist/src/routes/secretary.d.ts +7 -2
  35. package/dist/src/routes/secretary.d.ts.map +1 -1
  36. package/dist/src/routes/secretary.js +119 -28
  37. package/dist/src/routes/secretary.js.map +1 -1
  38. package/dist/src/services/onboarding-guide.d.ts +53 -0
  39. package/dist/src/services/onboarding-guide.d.ts.map +1 -0
  40. package/dist/src/services/onboarding-guide.js +81 -0
  41. package/dist/src/services/onboarding-guide.js.map +1 -0
  42. package/dist/src/services/scheduler.d.ts +9 -0
  43. package/dist/src/services/scheduler.d.ts.map +1 -1
  44. package/dist/src/services/scheduler.js +48 -23
  45. package/dist/src/services/scheduler.js.map +1 -1
  46. package/dist/src/services/secretary-workbrief.d.ts +144 -0
  47. package/dist/src/services/secretary-workbrief.d.ts.map +1 -0
  48. package/dist/src/services/secretary-workbrief.js +315 -0
  49. package/dist/src/services/secretary-workbrief.js.map +1 -0
  50. package/dist/src/services/secretary-worktier.d.ts +118 -0
  51. package/dist/src/services/secretary-worktier.d.ts.map +1 -0
  52. package/dist/src/services/secretary-worktier.js +157 -0
  53. package/dist/src/services/secretary-worktier.js.map +1 -0
  54. package/dist/src/services/skill-bundle/generator.d.ts +3 -0
  55. package/dist/src/services/skill-bundle/generator.d.ts.map +1 -1
  56. package/dist/src/services/skill-bundle/generator.js +19 -0
  57. package/dist/src/services/skill-bundle/generator.js.map +1 -1
  58. package/package.json +1 -1
@@ -559,7 +559,7 @@
559
559
  "wf": {
560
560
  "title": "Design a workflow",
561
561
  "quick": "Design workflow",
562
- "prompt": "Describe the outcome you want — I'll chain your specialists/agents into a workflow to reach it.",
562
+ "prompt": "Describe the outcome you want — I'll chain your specialists and Builders into a workflow (a multi-step pipeline) to reach it.",
563
563
  "placeholder": "e.g. Every evening, gather the day's news and publish an edition…",
564
564
  "design": "Design",
565
565
  "designing": "Designing the chain…",
@@ -742,10 +742,28 @@
742
742
  "apply": "Set up my Secretary",
743
743
  "applying": "Setting up…",
744
744
  "hireDone": "Your Secretary is set up.",
745
+ "briefsReady": "Work briefs ready to hand to a Builder",
745
746
  "fillingWorkspaces": "Filling your workspaces with real content…",
746
747
  "workspacesFilled": "Workspaces filled with real content",
748
+ "workspacesFilledIssues": "couldn't fill these yet",
747
749
  "emptyRemoved": "empty ones removed",
748
750
  "fillError": "Couldn't fill the workspaces",
751
+ "settingUpTitle": "Setting up your Secretary…",
752
+ "settingUpHint": "Hang tight — this can take a minute while it writes real content into each workspace.",
753
+ "stepStructure": "Creating your space and workspaces…",
754
+ "stepFilling": "Filling workspace",
755
+ "stepDone": "Done — finishing up…",
756
+ "mode": {
757
+ "title": "How should your Secretary work?",
758
+ "intro": "Either way, it fills your workspaces with real content right now. This choice is about what it does ON ITS OWN afterwards. You can change it any time.",
759
+ "draftTitle": "Prepare things for me (recommended)",
760
+ "draftDesc": "It keeps your workspaces up to date on its own and drafts what's next — but leaves it for you to approve. Nothing is published without your OK. Uses your AI credits.",
761
+ "actTitle": "Run things for me",
762
+ "actDesc": "Fully autonomous: it fills, updates and publishes on its own, every day, without asking. Uses your AI credits.",
763
+ "offTitle": "Only when I ask",
764
+ "offDesc": "It fills your workspaces now, then waits. It won't spend or act on its own until you turn it on.",
765
+ "changeLater": "You can change this any time under the Secretary's “How I work”."
766
+ },
749
767
  "hireError": "Couldn't set up the Secretary",
750
768
  "hireBadShape": "The pasted result is missing required fields (brain.purpose, organism.name, workspaces).",
751
769
  "hiredStatus": "Secretary is set up",
@@ -760,6 +778,13 @@
760
778
  "inappError": "Couldn't generate",
761
779
  "inappEmpty": "The AI returned nothing.",
762
780
  "howIWork": "How I work",
781
+ "autonomy": {
782
+ "suggest": "Suggest only",
783
+ "suggestHint": "I watch, keep things current, and surface what needs attention — but I ask before acting.",
784
+ "assist": "Handle the small stuff",
785
+ "assistHint": "I also file notes, post reminders and briefings on my own. Heavy work is always a Work Brief you hand to a Builder."
786
+ },
787
+ "advancedBands": "Advanced — per-capability control",
763
788
  "stopSpending": "Stop spending",
764
789
  "stopSpendingHint": "Pause everything that costs morsels or acts on its own. The Secretary can still think, draft, and message you.",
765
790
  "dailyBudget": "Daily morsel budget",
@@ -981,6 +1006,24 @@
981
1006
  "reliability": "Reliability",
982
1007
  "reliabilityBuilding": "building…",
983
1008
  "reliabilityHint": "Average score of the Secretary's reviewed decisions — its own track record, not its marketplace trust.",
1009
+ "brief": {
1010
+ "startHere": "👉 Start here:",
1011
+ "title": "Hand to a Builder",
1012
+ "subtitle": "Heavy work the Secretary prepared — a big AI builds it via MCP. You never invent content here.",
1013
+ "mcpNote": "Open one in Claude Desktop (it needs your AIMEAT MCP connected), or send it to an already-connected Builder.",
1014
+ "connectLink": "Connect a Builder",
1015
+ "heavyBadge": "Heavy",
1016
+ "dispatchedBadge": "Dispatched",
1017
+ "preview": "Preview what the Builder receives",
1018
+ "openClaude": "Open in Claude Desktop",
1019
+ "sendBuilder": "Send to a connected Builder…",
1020
+ "waiting": "Waiting on the Builder…",
1021
+ "dispatchedNote": "Sent — the Builder is working on it via MCP. This clears itself when the task finishes (or use Done).",
1022
+ "noBuilder": "Connect a Builder to dispatch",
1023
+ "done": "Done",
1024
+ "dispatched": "Brief sent to the Builder.",
1025
+ "dispatchFailed": "Couldn't dispatch the brief."
1026
+ },
984
1027
  "dash": {
985
1028
  "todayTitle": "Today",
986
1029
  "refresh": "Refresh",
@@ -1039,7 +1082,7 @@
1039
1082
  "startNewTitle": "Start something new",
1040
1083
  "nextStep": "Next step",
1041
1084
  "allSettled": "all steps settled",
1042
- "hint": "Tell me a goal — I'll propose a repeatable routine. Approve each step (band-gated) and I'll run it and track the results.",
1085
+ "hint": "Tell me a goal — I'll propose a repeatable routine: light, recurring checks I run and track myself (for heavy building, I hand a Builder a Work Brief instead). Approve each step (band-gated).",
1043
1086
  "placeholder": "What do you want to make progress on?",
1044
1087
  "propose": "Propose a routine",
1045
1088
  "proposing": "Designing…",
@@ -1072,7 +1115,9 @@
1072
1115
  "delegateWorkflowPick": "Run a workflow…",
1073
1116
  "delegateWorkflowGo": "Run workflow",
1074
1117
  "delegateDesignWf": "Design new workflow",
1075
- "delegatePick": "Pick an agent…",
1118
+ "delegatePick": "Pick who runs it…",
1119
+ "delegateSpecialists": "Specialists (node-local)",
1120
+ "delegateBuilders": "Builders (connected AI)",
1076
1121
  "delegateGo": "Delegate",
1077
1122
  "delegateNoAgent": "Pick an agent to delegate to first",
1078
1123
  "delegateNoAgents": "No other agents to delegate to — connect one in Manage → crew.",
@@ -8108,7 +8153,10 @@
8108
8153
  "complete_test_task": "Complete Test Task",
8109
8154
  "publish_commands": "Publish Slash Commands",
8110
8155
  "publish_config": "Publish Runtime Config",
8111
- "declare_services": "Declare Services"
8156
+ "declare_services": "Declare Services",
8157
+ "declare_offerings": "Declare Offerings",
8158
+ "make_workflow_compatible": "Make an Offer Workflow-Compatible",
8159
+ "price_offer": "Price an Offer"
8112
8160
  },
8113
8161
  "stepDescriptions": {
8114
8162
  "authenticate": "Agent is authenticated via device auth",
@@ -559,7 +559,7 @@
559
559
  "wf": {
560
560
  "title": "Suunnittele workflow",
561
561
  "quick": "Suunnittele workflow",
562
- "prompt": "Kuvaa haluttu lopputulema — ketjutan specialistisi/agenttisi workflow:ksi sen saavuttamiseksi.",
562
+ "prompt": "Kuvaa haluttu lopputulema — ketjutan specialistisi ja Builderit workflow:ksi (monivaiheinen putki) sen saavuttamiseksi.",
563
563
  "placeholder": "esim. Joka ilta kokoa päivän uutiset ja julkaise painos…",
564
564
  "design": "Suunnittele",
565
565
  "designing": "Suunnittelen ketjua…",
@@ -742,10 +742,28 @@
742
742
  "apply": "Pystytä sihteerini",
743
743
  "applying": "Pystytetään…",
744
744
  "hireDone": "Sihteerisi on pystytetty.",
745
+ "briefsReady": "Työbriiffit valmiina annettavaksi Builderille",
745
746
  "fillingWorkspaces": "Täytetään työtilojasi oikealla sisällöllä…",
746
747
  "workspacesFilled": "Työtilat täytetty oikealla sisällöllä",
748
+ "workspacesFilledIssues": "näitä ei voitu vielä täyttää",
747
749
  "emptyRemoved": "tyhjiä poistettu",
748
750
  "fillError": "Työtilojen täyttö epäonnistui",
751
+ "settingUpTitle": "Pystytetään sihteeriäsi…",
752
+ "settingUpHint": "Hetki — tämä voi kestää minuutin, kun se kirjoittaa oikeaa sisältöä jokaiseen työtilaan.",
753
+ "stepStructure": "Luodaan tila ja työtilat…",
754
+ "stepFilling": "Täytetään työtila",
755
+ "stepDone": "Valmis — viimeistellään…",
756
+ "mode": {
757
+ "title": "Miten sihteerisi toimii?",
758
+ "intro": "Kummassakin tapauksessa se täyttää työtilasi oikealla sisällöllä heti nyt. Tämä valinta koskee sitä mitä se tekee OMIN PÄIN sen jälkeen. Voit muuttaa tätä milloin vain.",
759
+ "draftTitle": "Valmistele puolestani (suositus)",
760
+ "draftDesc": "Pitää työtilat ajan tasalla itse ja luonnostelee seuraavat askeleet — mutta jättää ne sinun hyväksyttäväksesi. Mitään ei julkaista ilman lupaasi. Käyttää AI-krediittejäsi.",
761
+ "actTitle": "Hoida puolestani",
762
+ "actDesc": "Täysin autonominen: täyttää, päivittää ja julkaisee itse, joka päivä, kysymättä. Käyttää AI-krediittejäsi.",
763
+ "offTitle": "Vain kun pyydän",
764
+ "offDesc": "Täyttää työtilat nyt, sitten odottaa. Ei kuluta eikä toimi omin päin ennen kuin laitat sen päälle.",
765
+ "changeLater": "Voit muuttaa tätä milloin vain sihteerin ”How I work” -kohdasta."
766
+ },
749
767
  "hireError": "Sihteerin pystytys epäonnistui",
750
768
  "hireBadShape": "Liitetystä tuloksesta puuttuu pakollisia kenttiä (brain.purpose, organism.name, workspaces).",
751
769
  "hiredStatus": "Sihteeri on pystytetty",
@@ -760,6 +778,13 @@
760
778
  "inappError": "Luonti epäonnistui",
761
779
  "inappEmpty": "AI ei palauttanut mitään.",
762
780
  "howIWork": "Miten toimin",
781
+ "autonomy": {
782
+ "suggest": "Vain ehdota",
783
+ "suggestHint": "Seuraan, pidän asiat ajan tasalla ja nostan esiin mikä vaatii huomiota — mutta kysyn ennen kuin toimin.",
784
+ "assist": "Hoida pienet asiat",
785
+ "assistHint": "Kirjaan myös muistiinpanot, muistutukset ja briiffit itse. Raskas työ on aina Work Brief jonka annat Builderille."
786
+ },
787
+ "advancedBands": "Lisäasetukset — kyvykkyyskohtainen hallinta",
763
788
  "stopSpending": "Lopeta kulutus",
764
789
  "stopSpendingHint": "Keskeytä kaikki mikä kuluttaa morsseja tai toimii itsenäisesti. Sihteeri voi yhä ajatella, luonnostella ja viestiä sinulle.",
765
790
  "dailyBudget": "Päivittäinen morssi-budjetti",
@@ -981,6 +1006,24 @@
981
1006
  "reliability": "Luotettavuus",
982
1007
  "reliabilityBuilding": "kerätään…",
983
1008
  "reliabilityHint": "Sihteerin arvioitujen päätösten keskiarvopistemäärä — sen oma näyttö, ei markkinapaikan luottamus.",
1009
+ "brief": {
1010
+ "startHere": "👉 Aloita tästä:",
1011
+ "title": "Anna Builderille",
1012
+ "subtitle": "Raskas työ jonka Sihteeri valmisteli — iso äly rakentaa sen MCP:llä. Et keksi sisältöä täällä.",
1013
+ "mcpNote": "Avaa yksi Claude Desktopissa (siinä pitää olla AIMEAT-MCP kytkettynä), tai lähetä jo kytketylle Builderille.",
1014
+ "connectLink": "Kytke Builder",
1015
+ "heavyBadge": "Raskas",
1016
+ "dispatchedBadge": "Lähetetty",
1017
+ "preview": "Esikatsele mitä Builder saa",
1018
+ "openClaude": "Avaa Claude Desktopissa",
1019
+ "sendBuilder": "Lähetä kytketylle Builderille…",
1020
+ "waiting": "Odotetaan Builderia…",
1021
+ "dispatchedNote": "Lähetetty — Builder työstää sitä MCP:llä. Tämä poistuu itsestään kun task valmistuu (tai paina Valmis).",
1022
+ "noBuilder": "Kytke Builder lähettääksesi",
1023
+ "done": "Valmis",
1024
+ "dispatched": "Briiffi lähetetty Builderille.",
1025
+ "dispatchFailed": "Briiffin lähetys epäonnistui."
1026
+ },
984
1027
  "dash": {
985
1028
  "todayTitle": "Tänään",
986
1029
  "refresh": "Päivitä",
@@ -1039,7 +1082,7 @@
1039
1082
  "startNewTitle": "Aloita jotain uutta",
1040
1083
  "nextStep": "Seuraava vaihe",
1041
1084
  "allSettled": "kaikki vaiheet käsitelty",
1042
- "hint": "Kerro tavoite — ehdotan toistettavan rutiinin. Hyväksy jokainen vaihe (lupakaistan mukaan), niin ajan sen ja seuraan tuloksia.",
1085
+ "hint": "Kerro tavoite — ehdotan toistettavan rutiinin: kevyitä, toistuvia tarkistuksia jotka ajan ja seuraan itse (raskaaseen rakentamiseen annan Builderille Work Briefin). Hyväksy jokainen vaihe (lupakaistan mukaan).",
1043
1086
  "placeholder": "Missä haluat edistyä?",
1044
1087
  "propose": "Ehdota rutiinia",
1045
1088
  "proposing": "Suunnitellaan…",
@@ -1072,7 +1115,9 @@
1072
1115
  "delegateWorkflowPick": "Aja työnkulku…",
1073
1116
  "delegateWorkflowGo": "Aja työnkulku",
1074
1117
  "delegateDesignWf": "Suunnittele uusi workflow",
1075
- "delegatePick": "Valitse agentti…",
1118
+ "delegatePick": "Valitse kuka ajaa sen…",
1119
+ "delegateSpecialists": "Specialistit (node-paikalliset)",
1120
+ "delegateBuilders": "Builderit (kytketty AI)",
1076
1121
  "delegateGo": "Delegoi",
1077
1122
  "delegateNoAgent": "Valitse ensin agentti, jolle delegoidaan",
1078
1123
  "delegateNoAgents": "Ei muita agentteja delegoitavaksi — yhdistä sellainen kohdassa Hallinta → tiimi.",
@@ -8108,7 +8153,10 @@
8108
8153
  "complete_test_task": "Suorita testitehtävä",
8109
8154
  "publish_commands": "Julkaise slash-komennot",
8110
8155
  "publish_config": "Julkaise runtime-konfiguraatio",
8111
- "declare_services": "Ilmoita palvelut"
8156
+ "declare_services": "Ilmoita palvelut",
8157
+ "declare_offerings": "Ilmoita tarjoukset",
8158
+ "make_workflow_compatible": "Tee tarjouksesta työnkulkuyhteensopiva",
8159
+ "price_offer": "Hinnoittele tarjous"
8112
8160
  },
8113
8161
  "stepDescriptions": {
8114
8162
  "authenticate": "Agentti on tunnistautunut laitevarmennuksen kautta",
@@ -1016,3 +1016,55 @@
1016
1016
  display: flex; flex-direction: column; gap: 0.5rem;
1017
1017
  }
1018
1018
  .sec-modal .sec-input, .sec-modal .sec-stand-body { margin-top: 0.2rem; }
1019
+
1020
+ /* Live setup-progress overlay — a top-level modal shown through the whole setup (incl. the slow fill),
1021
+ surviving the hire→dashboard switch so the owner always sees what's happening and how far along it is. */
1022
+ .sec-setup-overlay { position: fixed; inset: 0; z-index: 3000; display: flex; align-items: center; justify-content: center;
1023
+ background: rgba(0, 0, 0, 0.45); padding: 1rem; }
1024
+ .sec-setup-box { background: var(--card); border: 1px solid var(--border); border-radius: 14px; box-shadow: var(--shadow);
1025
+ padding: 1.25rem 1.5rem; width: min(28rem, 96vw); display: flex; flex-direction: column; gap: 0.5rem; }
1026
+ .sec-setup-row { display: flex; align-items: center; gap: 0.5rem; font-weight: 600; margin: 0.25rem 0 0.5rem; }
1027
+ .sec-progress-bar { height: 8px; border-radius: 999px; background: var(--bg-dim, var(--border)); overflow: hidden; }
1028
+ .sec-progress-fill { height: 100%; background: var(--accent); border-radius: 999px; transition: width 0.4s ease; }
1029
+
1030
+ /* "How should your Secretary work?" autonomy step (asked once, up front, during setup). */
1031
+ .sec-mode-box { width: min(34rem, 96vw); }
1032
+ .sec-mode-opt { display: flex; gap: 0.6rem; align-items: flex-start; padding: 0.6rem 0.75rem; border: 1px solid var(--border);
1033
+ border-radius: 10px; cursor: pointer; }
1034
+ .sec-mode-opt + .sec-mode-opt { margin-top: 0.4rem; }
1035
+ .sec-mode-opt--on { border-color: var(--accent); background: var(--bg-dim, var(--card)); }
1036
+ .sec-mode-opt input { margin-top: 0.2rem; accent-color: var(--accent); }
1037
+ .sec-mode-opt-t { font-weight: 700; display: block; }
1038
+
1039
+ /* Work Brief tray — heavy work the Secretary prepared for a Builder (the light-vs-heavy seam). */
1040
+ .sec-brief-tray { border-color: var(--accent-border, var(--accent)); }
1041
+ .sec-brief-tray .sec-count { font-size: 0.78rem; font-weight: 600; padding: 0.05rem 0.45rem; border-radius: 999px;
1042
+ background: var(--accent-soft, var(--bg-dim)); color: var(--accent); margin-left: 0.35rem; }
1043
+ .sec-brief-banner { margin: 0 0 0.5rem; padding: 0.45rem 0.6rem; font-size: 0.82rem; color: var(--text-dim);
1044
+ background: var(--bg-dim); border: 1px solid var(--border); border-radius: var(--radius-sm, 8px); }
1045
+ .sec-brief-wait { margin-top: 0.3rem; }
1046
+ .sec-brief { padding: 0.65rem 0; border-top: 1px solid var(--border); }
1047
+ .sec-brief:first-of-type { border-top: none; }
1048
+ .sec-brief-top { display: flex; align-items: baseline; flex-wrap: wrap; gap: 0.45rem; }
1049
+ .sec-brief-obj { font-weight: 600; color: var(--text); }
1050
+ .sec-brief-ws { margin-left: auto; }
1051
+ .sec-brief-badge { font-size: 0.66rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.03em;
1052
+ padding: 0.05rem 0.4rem; border-radius: 999px; background: var(--accent-soft, var(--bg-dim));
1053
+ border: 1px solid var(--accent-border, var(--accent)); color: var(--accent); }
1054
+ .sec-brief-badge.is-dispatched { background: var(--info-bg, var(--bg-dim)); border-color: var(--info-border, var(--border)); color: var(--info-fg, var(--text-dim)); }
1055
+ .sec-brief-detail { margin: 0.4rem 0; }
1056
+ .sec-brief-detail > summary { cursor: pointer; color: var(--accent); font-size: 0.82rem; }
1057
+ .sec-brief-md { margin-top: 0.5rem; padding: 0.6rem 0.75rem; background: var(--bg-dim); border: 1px solid var(--border);
1058
+ border-radius: var(--radius-sm, 8px); max-height: 22rem; overflow: auto; font-size: 0.85rem; }
1059
+ .sec-brief-actions { display: flex; align-items: center; flex-wrap: wrap; gap: 0.5rem; margin-top: 0.35rem; }
1060
+ .sec-select { padding: 0.3rem 0.5rem; border: 1px solid var(--border); border-radius: var(--radius-sm, 8px);
1061
+ background: var(--card); color: var(--text); font-size: 0.82rem; }
1062
+
1063
+ /* One autonomy toggle (collapses the per-capability band sprawl). */
1064
+ .sec-autonomy { display: flex; flex-direction: column; gap: 0.4rem; margin-bottom: 0.75rem; }
1065
+ .sec-auto-opt { display: flex; gap: 0.6rem; align-items: flex-start; padding: 0.55rem 0.7rem; border: 1px solid var(--border);
1066
+ border-radius: 10px; cursor: pointer; }
1067
+ .sec-auto-opt--on { border-color: var(--accent); background: var(--bg-dim, var(--card)); }
1068
+ .sec-auto-opt input { margin-top: 0.2rem; accent-color: var(--accent); }
1069
+ .sec-adv { margin-top: 0.75rem; }
1070
+ .sec-adv > summary { cursor: pointer; color: var(--text-dim); font-size: 0.85rem; }
@@ -63,6 +63,15 @@ export function t(key, vars) {
63
63
  return str;
64
64
  }
65
65
 
66
+ /** Translate a key, but fall back to `fallbackText` when the key is missing.
67
+ * t() returns the raw key string on a miss (truthy), which defeats the common
68
+ * `t(key) || serverProvidedTitle` pattern -- use this when a server-provided
69
+ * label should win over a raw key. */
70
+ export function tOr(key, fallbackText, vars) {
71
+ const val = t(key, vars);
72
+ return val === key ? (fallbackText ?? key) : val;
73
+ }
74
+
66
75
  /** Get current locale. */
67
76
  export function getLocale() {
68
77
  return currentLocale;
@@ -8,6 +8,10 @@
8
8
  * @structure CONTRACT · buildInterviewPrompt · buildDesignPrompt · extractJson · snapshotOf · genCtxId · sanitizeQuickActions · migrateConfig · buildDecisionRecord
9
9
  * @usage import { buildInterviewPrompt, extractJson, sanitizeQuickActions, migrateConfig } from '/js/services/secretary-helpers.js';
10
10
  * @version-history
11
+ * v0.7.0 — 2026-06-30 — Sharpen the CONTRACT to state the rule plainly: DOCUMENTS ARE THE DEFAULT (most
12
+ * workspaces are flexible information → mostly/all document spaces); a RECORDS space is only for
13
+ * same-shaped, list-like data (actions/tasks/bugs/pipeline/tracker) and legitimately starts empty —
14
+ * so the user's AI stops over-producing rigid record spaces that the don't-invent fill leaves blank.
11
15
  * v0.6.0 — 2026-06-30 — CONTRACT steers the design toward DOCUMENT spaces (free-form markdown, the
12
16
  * default) and treats RECORDS spaces as the exception (only genuine lists/task-sets) — workspaces
13
17
  * were coming out mostly as rigid record spaces.
@@ -85,10 +89,10 @@ export const CONTRACT = `\`\`\`json
85
89
  }
86
90
  \`\`\`
87
91
 
88
- A workspace's "manifest.objectTypes" are its SPACES, and each space is one of two kinds. PREFER DOCUMENT spaces:
89
- - A DOCUMENT space — free-form markdown pages about a topic (guides, write-ups, notes, plans, analysis, anything). Declare it as { "name": "<type>", "namespace": "<ns>", "mode": "document" } with NO fields and NO schema. This is the DEFAULT choice: it holds any kind of information and can be shaped freely, so most spaces should be document spaces.
90
- - A RECORDS space — a schema-locked list where every entry shares the SAME fixed fields. Use this ONLY when the content is genuinely a list or task-like set (a pipeline, a task list, a tracker). Declare it as { "name": "<type>", "namespace": "<ns>", "mode": "records", "fields": [ { "name": "<field>", "type": "text|number|date|select|boolean|reference" } ] } AND provide a matching JSON Schema under "schemas" keyed by its namespace.
91
- Lean toward document spaces; reach for a records space only when fixed, repeating fields clearly fit. Full manifest shape: { "manifestVersion": "1.0", "name": "<the workspace name>", "kind": "<short-slug>", "status": "active", "objectTypes": [ …the spaces above ] }.
92
+ A workspace's "manifest.objectTypes" are its SPACES, and each space is one of two kinds. DOCUMENTS ARE THE DEFAULT — most workspaces are flexible information and should be all (or mostly) document spaces:
93
+ - A DOCUMENT space — free-form markdown pages about a topic (guides, write-ups, notes, plans, analysis, anything). Declare it as { "name": "<type>", "namespace": "<ns>", "mode": "document" } with NO fields and NO schema. This is the DEFAULT: it holds any kind of information and can be shaped and edited freely. When in doubt, use a document space.
94
+ - A RECORDS space — a schema-locked list where every entry shares the SAME fixed fields. Use this ONLY for genuinely list-like, repeating data: actions, tasks, bugs, a sales pipeline, a tracker — the SAME shape over and over, that you add rows to over time. Declare it as { "name": "<type>", "namespace": "<ns>", "mode": "records", "fields": [ { "name": "<field>", "type": "text|number|date|select|boolean|reference" } ] } AND provide a matching JSON Schema under "schemas" keyed by its namespace. A records space legitimately starts empty and fills as real items occur — so do not use one for content you want written up now.
95
+ Default to document spaces; reach for a records space only when same-shaped, list-like rows clearly fit. A workspace may be entirely document spaces. Full manifest shape: { "manifestVersion": "1.0", "name": "<the workspace name>", "kind": "<short-slug>", "status": "active", "objectTypes": [ …the spaces above ] }.
92
96
  If you are NOT confident you can produce a VALID manifest + schemas, OMIT both fields for that workspace — they will be designed automatically afterward. Never output an invalid or half-finished manifest.
93
97
 
94
98
  "strategy" is the OPTIONAL steering frame for this area: where we are, where we're going, and how. Include it ONLY when the user wants this area steered that way (many goal-driven areas benefit; some areas are just ongoing upkeep — then OMIT the field entirely). EVERY sub-field is itself optional — include only the ones the user gave you real material for; never invent vision/mission/progress/names. When included:
@@ -7,9 +7,14 @@
7
7
  * autonomous tick read it); for now it is authored + persisted here. Bands: act (do it + log) /
8
8
  * draft (prepare, ask to approve) / ask (decision card) / off. `discover` is locked to act + free
9
9
  * (read-only, never blocked by stop-spending). Full design: docs/plans/2026-06-23-secretary-feature.md §6/§9.
10
- * @structure BANDS · SECRETARY_CAPABILITIES · defaultPolicy() · mergePolicy()
11
- * @usage import { SECRETARY_CAPABILITIES, BANDS, defaultPolicy } from '/js/services/secretary-policy.js';
10
+ * @structure BANDS · SECRETARY_CAPABILITIES · LIGHT_AUTONOMY_CAPS · defaultPolicy() · mergePolicy() · autonomyPreset() · autonomyLevelOf()
11
+ * @usage import { SECRETARY_CAPABILITIES, BANDS, defaultPolicy, autonomyPreset, autonomyLevelOf } from '/js/services/secretary-policy.js';
12
12
  * @version-history
13
+ * v0.3.0 — 2026-07-01 — Light-vs-heavy seam: dropped `author_content` (heavy authoring is now a Work
14
+ * Brief a Builder executes, never a band). Added the two-state autonomy preset (autonomyPreset /
15
+ * autonomyLevelOf + LIGHT_AUTONOMY_CAPS) so the UI can show ONE toggle (suggest-only vs handle-the-
16
+ * small-stuff) that sets the underlying per-capability bands — the band machinery + parity tests are
17
+ * unchanged; the toggle is sugar over the same bands map.
13
18
  * v0.2.0 — 2026-06-24 — Phase 3d: mark Enterprise-only capabilities (third-party messaging, transacting,
14
19
  * spending) with `enterprise: true` — gated/locked in the Community Secretary (which lacks the
15
20
  * messages:send / work:request / wallet scopes). The explicit third-party-send gate.
@@ -31,7 +36,6 @@ export const SECRETARY_CAPABILITIES = [
31
36
  { id: 'file_intake', defaultBand: 'act' },
32
37
  { id: 'briefing', defaultBand: 'act' },
33
38
  { id: 'reminders', defaultBand: 'act' },
34
- { id: 'author_content', defaultBand: 'off', costs: true },
35
39
  { id: 'curate_knowledge', defaultBand: 'draft' },
36
40
  { id: 'draft_replies', defaultBand: 'draft' },
37
41
  { id: 'create_resource', defaultBand: 'ask' },
@@ -41,6 +45,10 @@ export const SECRETARY_CAPABILITIES = [
41
45
  { id: 'spend', defaultBand: 'ask', costs: true, enterprise: true },
42
46
  ];
43
47
 
48
+ /** The LIGHT capabilities the one autonomy toggle flips: in "assist" they act, in "suggest" they only
49
+ * surface (ask). Everything else keeps its conservative default — heavy work is never a band, it's a Brief. */
50
+ export const LIGHT_AUTONOMY_CAPS = ['file_intake', 'briefing', 'reminders', 'curate_knowledge'];
51
+
44
52
  /** The conservative default operating model seeded when the Secretary is hired. */
45
53
  export function defaultPolicy() {
46
54
  const bands = {};
@@ -48,6 +56,23 @@ export function defaultPolicy() {
48
56
  return { stopSpending: false, dailyMorselBudget: null, bands };
49
57
  }
50
58
 
59
+ /** The two-state autonomy preset behind the single UI toggle. 'suggest' = the Secretary only surfaces
60
+ * things for you (light caps → ask); 'assist' = it handles the small stuff itself (light caps → act).
61
+ * Returns a full bands map (the other capabilities keep their conservative defaults). */
62
+ export function autonomyPreset(level) {
63
+ const bands = {};
64
+ for (const c of SECRETARY_CAPABILITIES) bands[c.id] = c.defaultBand;
65
+ const lightBand = level === 'assist' ? 'act' : 'ask';
66
+ for (const id of LIGHT_AUTONOMY_CAPS) if (bands[id] !== undefined) bands[id] = lightBand;
67
+ return bands;
68
+ }
69
+
70
+ /** Which preset a stored bands map looks like: 'assist' iff every light cap is acting, else 'suggest'. */
71
+ export function autonomyLevelOf(bands) {
72
+ const b = bands || {};
73
+ return LIGHT_AUTONOMY_CAPS.every((id) => b[id] === 'act') ? 'assist' : 'suggest';
74
+ }
75
+
51
76
  /** Merge a stored (possibly partial / older) policy onto the current defaults so new capabilities
52
77
  * always have a band and locked ones stay correct. */
53
78
  export function mergePolicy(stored) {
@@ -3,6 +3,8 @@
3
3
  * @description Agent card component with collapsed/expanded states,
4
4
  * Two-Zone Header (identity + state-dependent status), and tab bar.
5
5
  * @version-history
6
+ * v1.18.0 -- 2026-06-30 -- Onboarding step labels use tOr() so a missing agentOnboarding.steps.*
7
+ * key falls back to the server-provided step.title instead of rendering the raw key.
6
8
  * v1.17.0 -- 2026-06-24 -- Secretary P5 (S-A): render a "Specialist · <role>" badge for agents tagged
7
9
  * system:specialist, so the new specialist agent type is recognizable in the Agents tab.
8
10
  * v1.16.0 -- 2026-06-21 -- Unseen-change badges are now has-unseen DOTS (no count) — the
@@ -59,7 +61,7 @@
59
61
  import { h } from 'preact';
60
62
  import { useState, useEffect, useCallback, useRef } from 'preact/hooks';
61
63
  import htm from 'htm';
62
- import { t } from '/js/i18n.js';
64
+ import { t, tOr } from '/js/i18n.js';
63
65
  import { apiGet, apiPatch } from '/js/api.js';
64
66
  import { timeAgo } from '/js/utils.js';
65
67
  import { detectAgentState, getDefaultTab, getStateColor } from './state-detector.js';
@@ -437,7 +439,7 @@ function renderCollapsedStats(state, agent, onboarding) {
437
439
  return html`${agent.last_seen ? `${t('profile.agents.detail.lastSeen')}: ${timeAgo(agent.last_seen)}` : ''} | ${t('profile.agents.detail.state.newSummary')}`;
438
440
  case 'onboarding': {
439
441
  const nextStep = onboarding?.steps?.find(s => s.status === 'pending');
440
- return html`${agent.last_seen ? `${t('profile.agents.detail.lastSeen')}: ${timeAgo(agent.last_seen)}` : ''} ${nextStep ? `| ${t('profile.agents.detail.state.next')}: ${t('agentOnboarding.steps.' + nextStep.id) || nextStep.title || nextStep.id}` : ''}`;
442
+ return html`${agent.last_seen ? `${t('profile.agents.detail.lastSeen')}: ${timeAgo(agent.last_seen)}` : ''} ${nextStep ? `| ${t('profile.agents.detail.state.next')}: ${tOr('agentOnboarding.steps.' + nextStep.id, nextStep.title || nextStep.id)}` : ''}`;
441
443
  }
442
444
  case 'problem':
443
445
  return html`${t('profile.agents.detail.state.problemSummary')} | ${agent.last_seen ? `${t('profile.agents.detail.lastSeen')}: ${timeAgo(agent.last_seen)}` : ''}`;
@@ -483,7 +485,7 @@ function renderZone2(state, agent, onboarding, setActiveTab, showToast) {
483
485
  <div class="pf-agd-zone2 pf-agd-zone2--onboarding">
484
486
  <div class="pf-agd-zone2-title">
485
487
  ${t('profile.agents.detail.zone2.onboardingTitle')}: ${passed} / ${total}
486
- ${nextStep ? html`<span class="pf-agd-zone2-desc"> ${t('profile.agents.detail.state.next')}: ${t('agentOnboarding.steps.' + nextStep.id) || nextStep.title || nextStep.id}</span>` : ''}
488
+ ${nextStep ? html`<span class="pf-agd-zone2-desc"> ${t('profile.agents.detail.state.next')}: ${tOr('agentOnboarding.steps.' + nextStep.id, nextStep.title || nextStep.id)}</span>` : ''}
487
489
  </div>
488
490
  <div class="pf-agd-progress-bar">
489
491
  <div class="pf-agd-progress-fill" style="width: ${pct}%"></div>
@@ -491,7 +493,7 @@ function renderZone2(state, agent, onboarding, setActiveTab, showToast) {
491
493
  <div class="pf-agd-step-pills">
492
494
  ${steps.map(s => html`
493
495
  <span key=${s.id} class="pf-agd-step-pill pf-agd-step-pill--${s.status}">
494
- ${s.status === 'passed' ? '✓' : '○'} ${t('agentOnboarding.steps.' + s.id) || s.title || s.id}
496
+ ${s.status === 'passed' ? '✓' : '○'} ${tOr('agentOnboarding.steps.' + s.id, s.title || s.id)}
495
497
  </span>
496
498
  `)}
497
499
  </div>
@@ -4,6 +4,9 @@
4
4
  * during onboarding or production status (connection, platform, readiness,
5
5
  * identity, delivery log) after completion.
6
6
  * @version-history
7
+ * v1.8.0 -- 2026-06-30 -- Onboarding checklist step labels use tOr() so a missing
8
+ * agentOnboarding.steps.* key falls back to the server-provided step.title (which carries
9
+ * the howTo-enriched onboarding payload) instead of rendering the raw i18n key.
7
10
  * v1.7.0 -- 2026-06-10 -- Webhook fields (URL, fail count, Test/Edit) hidden while delivery
8
11
  * is polling; a "+ Set up webhook" reveal opens the URL form for switching methods.
9
12
  * v1.6.0 -- 2026-06-02 -- Component unification (#1): the 3 copy buttons (agent prompt,
@@ -29,7 +32,7 @@ import { h } from 'preact';
29
32
  import { useState, useEffect, useRef } from 'preact/hooks';
30
33
  import htm from 'htm';
31
34
  import { onLiveUpdate } from '/lib/live-updates.js';
32
- import { t } from '/js/i18n.js';
35
+ import { t, tOr } from '/js/i18n.js';
33
36
  import { timeAgo } from '/js/utils.js';
34
37
  import { CopyButton } from '/components/CopyButton.js';
35
38
  import { detectAgentState } from './state-detector.js';
@@ -241,7 +244,7 @@ function renderOnboardingView(onboarding, agentName, handleRerun, rerunning, cur
241
244
  <span class="pf-agd-step-icon">
242
245
  ${step.status === 'passed' ? '✓' : step.status === 'failed' ? '✗' : '○'}
243
246
  </span>
244
- <span class="pf-agd-step-name">${i + 1}. ${t('agentOnboarding.steps.' + step.id) || step.title || step.id}</span>
247
+ <span class="pf-agd-step-name">${i + 1}. ${tOr('agentOnboarding.steps.' + step.id, step.title || step.id)}</span>
245
248
  <span class="pf-agd-step-detail">
246
249
  ${step.validatedAt ? timeAgo(step.validatedAt) : ''}
247
250
  ${step.validationDetail ? ` -- ${step.validationDetail}` : ''}
@@ -30,7 +30,7 @@ const html = htm.bind(h);
30
30
  import { t } from '/js/i18n.js';
31
31
  import { CopyButton } from '/components/CopyButton.js';
32
32
  import { Markdown } from '/components/Markdown.js';
33
- import { SECRETARY_CAPABILITIES, BANDS } from '/js/services/secretary-policy.js';
33
+ import { SECRETARY_CAPABILITIES, BANDS, autonomyLevelOf } from '/js/services/secretary-policy.js';
34
34
  import { buildInterviewPrompt, groupFeedByDay } from '/js/services/secretary-helpers.js';
35
35
 
36
36
  /** Context "hats" switcher chips + add. */
@@ -227,9 +227,22 @@ export function brainCard(p) {
227
227
  /** Operating model: stop-spending + daily budget + per-capability autonomy bands. */
228
228
  export function operatingCard(p) {
229
229
  const policy = p.policy;
230
+ const level = autonomyLevelOf(policy.bands); // 'suggest' | 'assist' — the single toggle's state
230
231
  return html`
231
232
  <section class="sec-card">
232
233
  <h2 class="sec-h2">${t('secretary.howIWork')}</h2>
234
+
235
+ ${/* ONE autonomy toggle (collapses the per-capability band sprawl). Heavy work is always a Work
236
+ Brief a Builder executes — never something the cheap Secretary does on its own. */ ''}
237
+ <div class="sec-autonomy">
238
+ ${[['suggest', 'secretary.autonomy.suggest', 'secretary.autonomy.suggestHint'],
239
+ ['assist', 'secretary.autonomy.assist', 'secretary.autonomy.assistHint']].map(([val, label, hint]) => html`
240
+ <label class=${'sec-auto-opt' + (level === val ? ' sec-auto-opt--on' : '')} key=${val}>
241
+ <input type="radio" name="sec-autonomy" checked=${level === val} onChange=${() => p.setAutonomy(val)} />
242
+ <span><strong>${t(label)}</strong><br/><span class="sec-hint">${t(hint)}</span></span>
243
+ </label>`)}
244
+ </div>
245
+
233
246
  <label class="sec-stop ${policy.stopSpending ? 'on' : ''}">
234
247
  <input type="checkbox" checked=${policy.stopSpending} onChange=${p.toggleStop} />
235
248
  <span><strong>${t('secretary.stopSpending')}</strong><br/><span class="sec-hint">${t('secretary.stopSpendingHint')}</span></span>
@@ -239,18 +252,24 @@ export function operatingCard(p) {
239
252
  <input type="number" min="0" class="sec-budget-in" value=${policy.dailyMorselBudget == null ? '' : policy.dailyMorselBudget} placeholder=${t('secretary.noLimit')} onChange=${(e) => p.setBudget(e.target.value)} />
240
253
  <span class="sec-hint">${t('secretary.dailyBudgetHint')}</span>
241
254
  </div>
242
- <p class="sec-hint sec-bands-note">${t('secretary.bandsNote')}</p>
243
- <ul class="sec-caps">
244
- ${SECRETARY_CAPABILITIES.map((c) => html`
245
- <li class="sec-cap" key=${c.id}>
246
- <span class="sec-cap-label">${t('secretary.cap.' + c.id)}${c.costs ? html`<span class="sec-cap-tag">${t('secretary.capCosts')}</span>` : null}</span>
247
- ${c.enterprise
248
- ? html`<span class="sec-cap-locked sec-cap-ent">${t('secretary.enterprise')}</span>`
249
- : c.locked
250
- ? html`<span class="sec-cap-locked">${t('secretary.band.' + policy.bands[c.id])} · ${t('secretary.locked')}</span>`
251
- : html`<select class="sec-band" value=${policy.bands[c.id]} onChange=${(e) => p.setBand(c.id, e.target.value)}>${BANDS.map((b) => html`<option value=${b} selected=${policy.bands[c.id] === b}>${t('secretary.band.' + b)}</option>`)}</select>`}
252
- </li>`)}
253
- </ul>
255
+
256
+ ${/* Per-capability bands are still here for power users, tucked behind an Advanced disclosure so the
257
+ default surface is just the one toggle above. */ ''}
258
+ <details class="sec-adv">
259
+ <summary>${t('secretary.advancedBands')}</summary>
260
+ <p class="sec-hint sec-bands-note">${t('secretary.bandsNote')}</p>
261
+ <ul class="sec-caps">
262
+ ${SECRETARY_CAPABILITIES.map((c) => html`
263
+ <li class="sec-cap" key=${c.id}>
264
+ <span class="sec-cap-label">${t('secretary.cap.' + c.id)}${c.costs ? html`<span class="sec-cap-tag">${t('secretary.capCosts')}</span>` : null}</span>
265
+ ${c.enterprise
266
+ ? html`<span class="sec-cap-locked sec-cap-ent">${t('secretary.enterprise')}</span>`
267
+ : c.locked
268
+ ? html`<span class="sec-cap-locked">${t('secretary.band.' + policy.bands[c.id])} · ${t('secretary.locked')}</span>`
269
+ : html`<select class="sec-band" value=${policy.bands[c.id]} onChange=${(e) => p.setBand(c.id, e.target.value)}>${BANDS.map((b) => html`<option value=${b} selected=${policy.bands[c.id] === b}>${t('secretary.band.' + b)}</option>`)}</select>`}
270
+ </li>`)}
271
+ </ul>
272
+ </details>
254
273
  </section>`;
255
274
  }
256
275
 
@@ -278,13 +297,25 @@ function routineStepRow(p, r, s) {
278
297
  const isDelegate = s.capability === 'delegate';
279
298
  const agents = p.agents || [];
280
299
  const workflows = p.workflows || [];
300
+ // Unblur the delegation target by ROLE: node-local Specialists vs connected big-AI Builders. The Secretary
301
+ // (the watcher) is never a delegation target. Same handler/value (agent name) — the optgroups just label
302
+ // which kind of agent each is, so "pick a specialist" no longer lumps Builders + specialists together.
303
+ const isSpecialist = (a) => (a.tags || []).includes('system:specialist');
304
+ const isSecretary = (a) => (a.tags || []).includes('system:secretary') || a.name === 'secretary';
305
+ const specialistAgents = agents.filter((a) => isSpecialist(a));
306
+ const builderAgents = agents.filter((a) => !isSpecialist(a) && !isSecretary(a));
281
307
  // B4: delegate to one agent (agent task) · G7: OR run an Agent Workflow chaining specialists.
282
308
  const delegateControl = html`
283
309
  <div class="sec-delegate">
284
- ${agents.length > 0 ? html`
310
+ ${(specialistAgents.length + builderAgents.length) > 0 ? html`
285
311
  <select class="sec-band" value=${p.delegateAgent} onChange=${(e) => p.setDelegateAgent(e.target.value)}>
286
312
  <option value="" selected=${!p.delegateAgent}>${t('secretary.next.delegatePick')}</option>
287
- ${agents.map((a) => html`<option value=${a.name} selected=${p.delegateAgent === a.name}>${(a.name)}</option>`)}
313
+ ${specialistAgents.length ? html`<optgroup label=${t('secretary.next.delegateSpecialists')}>
314
+ ${specialistAgents.map((a) => html`<option value=${a.name} selected=${p.delegateAgent === a.name}>${(a.name)}</option>`)}
315
+ </optgroup>` : null}
316
+ ${builderAgents.length ? html`<optgroup label=${t('secretary.next.delegateBuilders')}>
317
+ ${builderAgents.map((a) => html`<option value=${a.name} selected=${p.delegateAgent === a.name}>${(a.name)}</option>`)}
318
+ </optgroup>` : null}
288
319
  </select>
289
320
  <button class="btn-primary btn-sm" disabled=${busy || !p.delegateAgent} onClick=${() => p.approveStep(r, s, { agentName: p.delegateAgent })}>${busy ? t('secretary.next.running') : t('secretary.next.delegateGo')}</button>` : null}
290
321
  ${workflows.length > 0 ? html`