@xopcai/xopc 0.0.89 → 0.0.91
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/README.md +36 -12
- package/README.zh-CN.md +36 -12
- package/dist/browser-ext/manifest.json +1 -1
- package/dist/extensions/telegram/xopc.extension.json +1 -1
- package/dist/gateway/static/root/assets/Combination-HAlzriaz.js +41 -0
- package/dist/gateway/static/root/assets/agents-bVWUlrlD.js +222 -0
- package/dist/gateway/static/root/assets/apps-page-CIC8bmvZ.js +1 -0
- package/dist/gateway/static/root/assets/{attachment-preview-renderer-CpyoFbs4.js → attachment-preview-renderer-DBAxQXb-.js} +2 -2
- package/dist/gateway/static/root/assets/{attachment-process-heavy-CqVriadb.js → attachment-process-heavy-Csq3TrrP.js} +4 -4
- package/dist/gateway/static/root/assets/channels-settings-C8G8RAAP.js +1 -0
- package/dist/gateway/static/root/assets/{channels-status-swr-DaHGkRF1.js → channels-status-swr-CYWL5DLD.js} +1 -1
- package/dist/gateway/static/root/assets/circle-check-C23XjkUj.js +1 -0
- package/dist/gateway/static/root/assets/copy-Dv6d4Dvw.js +1 -0
- package/dist/gateway/static/root/assets/cron-api-TVqLlGAC.js +1 -0
- package/dist/gateway/static/root/assets/cron-dreaming-jobs-Ip703-qM.js +2 -0
- package/dist/gateway/static/root/assets/cron-page-BtcFYlvv.js +1 -0
- package/dist/gateway/static/root/assets/dist-CUV1uY5f.js +1 -0
- package/dist/gateway/static/root/assets/{extension-debug-page-CtuKJ9tE.js → extension-debug-page-mTLHRDp1.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-page-ykzjOkR5.js → extension-page-iI8BI7WK.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-settings-page-Ce2qrdpO.js → extension-settings-page-ByXcdubM.js} +1 -1
- package/dist/gateway/static/root/assets/{fetch-C9FFJjuH.js → fetch-BWtQq_Ys.js} +1 -1
- package/dist/gateway/static/root/assets/{field-primitives-BFcrNeTU.js → field-primitives-BsZ-4VT5.js} +1 -1
- package/dist/gateway/static/root/assets/{heartbeat-config-api-CEg4Vr9R.js → heartbeat-config-api-WjTsRLCU.js} +1 -1
- package/dist/gateway/static/root/assets/{index-CZfy9oxs.js → index-CKkR-v9U.js} +101 -97
- package/dist/gateway/static/root/assets/index-VlELBY99.css +1 -0
- package/dist/gateway/static/root/assets/logs-page-ClnIpxfd.js +1 -0
- package/dist/gateway/static/root/assets/note-detail-page-B91pLkEI.css +1 -0
- package/dist/gateway/static/root/assets/note-detail-page-DJ2Mb4x7.js +179 -0
- package/dist/gateway/static/root/assets/note-time-JLBPSLzK.js +1 -0
- package/dist/gateway/static/root/assets/notes-page-BE-75qz9.js +1 -0
- package/dist/gateway/static/root/assets/{pdf-BnEvgIXZ.js → pdf-epILhEOn.js} +1 -1
- package/dist/gateway/static/root/assets/preload-helper-zJ_50EbN.js +1 -0
- package/dist/gateway/static/root/assets/sessions-page-bJJkWtTl.js +1 -0
- package/dist/gateway/static/root/assets/{settings-form-section-BqdzA28u.js → settings-form-section-DSYCknxM.js} +1 -1
- package/dist/gateway/static/root/assets/settings-page-WcMXLq2U.js +3 -0
- package/dist/gateway/static/root/assets/share-preview-page-awRqs4hV.js +2 -0
- package/dist/gateway/static/root/assets/skills-page-Lu-i1JG7.js +2 -0
- package/dist/gateway/static/root/assets/{theme-store-CNqbmTNV.js → theme-store-BC-42BoZ.js} +1 -1
- package/dist/gateway/static/root/assets/toast-z0toXu32.js +1 -0
- package/dist/gateway/static/root/assets/url-CY1RQKTU.js +3 -0
- package/dist/gateway/static/root/assets/{utils-BWm2tG2w.js → utils-DX3TQuap.js} +1 -1
- package/dist/gateway/static/root/assets/vendor-codemirror-DYoKfS8f.js +45 -0
- package/dist/gateway/static/root/assets/voice-api-key-field-B5uKlDqA.js +1 -0
- package/dist/gateway/static/root/assets/workflow-page.utils-ClC37yEp.js +1 -0
- package/dist/gateway/static/root/assets/workflows-page-C7VhIXtR.js +27 -0
- package/dist/gateway/static/root/index.html +11 -7
- package/dist/package.js +1 -1
- package/dist/src/agent/skills/marketplace/adapters/skillhub/adapter.js +20 -18
- package/dist/src/agent/skills/marketplace/adapters/skillhub/adapter.js.map +1 -1
- package/dist/src/agent/tools/cronjob-tool.d.ts +6 -0
- package/dist/src/agent/tools/cronjob-tool.js +74 -9
- package/dist/src/agent/tools/cronjob-tool.js.map +1 -1
- package/dist/src/agent/tools/edit.d.ts +5 -1
- package/dist/src/agent/tools/edit.js +7 -5
- package/dist/src/agent/tools/edit.js.map +1 -1
- package/dist/src/agent/tools/factory.js +2 -2
- package/dist/src/agent/tools/factory.js.map +1 -1
- package/dist/src/agent/tools/write.d.ts +5 -1
- package/dist/src/agent/tools/write.js +7 -5
- package/dist/src/agent/tools/write.js.map +1 -1
- package/dist/src/agent/workflow/agent-progress.js +2 -0
- package/dist/src/agent/workflow/agent-progress.js.map +1 -1
- package/dist/src/agent/workflow/builtins/client-proposal.d.ts +12 -0
- package/dist/src/agent/workflow/builtins/client-proposal.js +155 -0
- package/dist/src/agent/workflow/builtins/client-proposal.js.map +1 -0
- package/dist/src/agent/workflow/builtins/competitor-scan.d.ts +12 -0
- package/dist/src/agent/workflow/builtins/competitor-scan.js +150 -0
- package/dist/src/agent/workflow/builtins/competitor-scan.js.map +1 -0
- package/dist/src/agent/workflow/builtins/content-draft.d.ts +13 -0
- package/dist/src/agent/workflow/builtins/content-draft.js +146 -0
- package/dist/src/agent/workflow/builtins/content-draft.js.map +1 -0
- package/dist/src/agent/workflow/builtins/content-repurpose.d.ts +11 -0
- package/dist/src/agent/workflow/builtins/content-repurpose.js +137 -0
- package/dist/src/agent/workflow/builtins/content-repurpose.js.map +1 -0
- package/dist/src/agent/workflow/builtins/decision-compare.d.ts +13 -0
- package/dist/src/agent/workflow/builtins/decision-compare.js +173 -0
- package/dist/src/agent/workflow/builtins/decision-compare.js.map +1 -0
- package/dist/src/agent/workflow/builtins/inbox-triage.d.ts +11 -0
- package/dist/src/agent/workflow/builtins/inbox-triage.js +148 -0
- package/dist/src/agent/workflow/builtins/inbox-triage.js.map +1 -0
- package/dist/src/agent/workflow/builtins/index.d.ts +10 -1
- package/dist/src/agent/workflow/builtins/index.js +46 -1
- package/dist/src/agent/workflow/builtins/index.js.map +1 -1
- package/dist/src/agent/workflow/builtins/meeting-prep.d.ts +12 -0
- package/dist/src/agent/workflow/builtins/meeting-prep.js +144 -0
- package/dist/src/agent/workflow/builtins/meeting-prep.js.map +1 -0
- package/dist/src/agent/workflow/builtins/offer-design.d.ts +12 -0
- package/dist/src/agent/workflow/builtins/offer-design.js +161 -0
- package/dist/src/agent/workflow/builtins/offer-design.js.map +1 -0
- package/dist/src/agent/workflow/builtins/weekly-review.d.ts +12 -0
- package/dist/src/agent/workflow/builtins/weekly-review.js +131 -0
- package/dist/src/agent/workflow/builtins/weekly-review.js.map +1 -0
- package/dist/src/agent/workflow/step-labels.js +2 -2
- package/dist/src/agent/workflow/step-labels.js.map +1 -1
- package/dist/src/agent/workflow/subagent-runner.js +3 -1
- package/dist/src/agent/workflow/subagent-runner.js.map +1 -1
- package/dist/src/agent/workflow/types.d.ts +4 -0
- package/dist/src/chat-commands/agent-edit.d.ts +4 -0
- package/dist/src/chat-commands/agent-edit.js +136 -0
- package/dist/src/chat-commands/agent-edit.js.map +1 -0
- package/dist/src/chat-commands/index.d.ts +1 -0
- package/dist/src/chat-commands/index.js +3 -1
- package/dist/src/chat-commands/index.js.map +1 -1
- package/dist/src/cli/bin.js +2 -0
- package/dist/src/cli/bin.js.map +1 -1
- package/dist/src/cli/commands/cron.js +42 -3
- package/dist/src/cli/commands/cron.js.map +1 -1
- package/dist/src/cli/commands/doctor/checks/session-integrity.js +79 -56
- package/dist/src/cli/commands/doctor/checks/session-integrity.js.map +1 -1
- package/dist/src/cli/commands/gateway/lifecycle.js +1 -1
- package/dist/src/cli/commands/update.js +86 -79
- package/dist/src/cli/commands/update.js.map +1 -1
- package/dist/src/commands/agents.config.d.ts +3 -2
- package/dist/src/commands/agents.config.js +5 -2
- package/dist/src/commands/agents.config.js.map +1 -1
- package/dist/src/config/agent-typed-models.d.ts +2 -7
- package/dist/src/config/agent-typed-models.js +3 -14
- package/dist/src/config/agent-typed-models.js.map +1 -1
- package/dist/src/config/localized-text.d.ts +6 -0
- package/dist/src/config/localized-text.js +42 -0
- package/dist/src/config/localized-text.js.map +1 -0
- package/dist/src/config/models-json.d.ts +6 -6
- package/dist/src/config/schema.d.ts +6 -21
- package/dist/src/config/schema.js +4 -4
- package/dist/src/config/schema.js.map +1 -1
- package/dist/src/cron/executor.d.ts +2 -0
- package/dist/src/cron/executor.js +111 -1
- package/dist/src/cron/executor.js.map +1 -1
- package/dist/src/cron/types.d.ts +8 -1
- package/dist/src/cron/validation.d.ts +4 -0
- package/dist/src/cron/validation.js +4 -3
- package/dist/src/cron/validation.js.map +1 -1
- package/dist/src/cron/workflow-run-completion.d.ts +23 -0
- package/dist/src/cron/workflow-run-completion.js +72 -0
- package/dist/src/cron/workflow-run-completion.js.map +1 -0
- package/dist/src/extensions/update.d.ts +51 -0
- package/dist/src/extensions/update.js +260 -0
- package/dist/src/extensions/update.js.map +1 -0
- package/dist/src/gateway/agents-admin.d.ts +15 -8
- package/dist/src/gateway/agents-admin.js +77 -28
- package/dist/src/gateway/agents-admin.js.map +1 -1
- package/dist/src/gateway/heartbeat/service.js +1 -1
- package/dist/src/gateway/hono/lib/config-payload.d.ts +6 -0
- package/dist/src/gateway/hono/lib/config-payload.js +3 -1
- package/dist/src/gateway/hono/lib/config-payload.js.map +1 -1
- package/dist/src/gateway/hono/middleware/auth.d.ts +2 -0
- package/dist/src/gateway/hono/middleware/auth.js +11 -7
- package/dist/src/gateway/hono/middleware/auth.js.map +1 -1
- package/dist/src/gateway/hono/routes/agents.js +55 -12
- package/dist/src/gateway/hono/routes/agents.js.map +1 -1
- package/dist/src/gateway/hono/routes/config-patch/agents.js +1 -1
- package/dist/src/gateway/hono/routes/config-patch/gateway.d.ts +2 -2
- package/dist/src/gateway/hono/routes/config-patch/gateway.js +12 -0
- package/dist/src/gateway/hono/routes/config-patch/gateway.js.map +1 -1
- package/dist/src/gateway/hono/routes/lazy-bundles.js +8 -0
- package/dist/src/gateway/hono/routes/lazy-bundles.js.map +1 -1
- package/dist/src/gateway/hono/routes/notes.d.ts +3 -0
- package/dist/src/gateway/hono/routes/notes.js +274 -0
- package/dist/src/gateway/hono/routes/notes.js.map +1 -0
- package/dist/src/gateway/hono/routes/sessions.js.map +1 -1
- package/dist/src/gateway/hono/routes/update.js +55 -107
- package/dist/src/gateway/hono/routes/update.js.map +1 -1
- package/dist/src/gateway/hono/routes/workflows.js +3 -1
- package/dist/src/gateway/hono/routes/workflows.js.map +1 -1
- package/dist/src/gateway/server.js +2 -0
- package/dist/src/gateway/server.js.map +1 -1
- package/dist/src/gateway/service.d.ts +3 -0
- package/dist/src/gateway/service.js +12 -1
- package/dist/src/gateway/service.js.map +1 -1
- package/dist/src/gateway/workspace-ripgrep.d.ts +6 -0
- package/dist/src/gateway/workspace-ripgrep.js +62 -11
- package/dist/src/gateway/workspace-ripgrep.js.map +1 -1
- package/dist/src/heartbeat/index.js +1 -1
- package/dist/src/infra/brew.d.ts +4 -0
- package/dist/src/infra/brew.js +20 -0
- package/dist/src/infra/brew.js.map +1 -0
- package/dist/src/infra/package-json.d.ts +2 -0
- package/dist/src/infra/package-json.js +23 -0
- package/dist/src/infra/package-json.js.map +1 -0
- package/dist/src/infra/package-update-steps.d.ts +35 -0
- package/dist/src/infra/package-update-steps.js +304 -0
- package/dist/src/infra/package-update-steps.js.map +1 -0
- package/dist/src/infra/path-env.d.ts +11 -0
- package/dist/src/infra/path-env.js +90 -0
- package/dist/src/infra/path-env.js.map +1 -0
- package/dist/src/infra/path-prepend.d.ts +7 -0
- package/dist/src/infra/path-prepend.js +44 -0
- package/dist/src/infra/path-prepend.js.map +1 -0
- package/dist/src/infra/stable-node-path.d.ts +2 -0
- package/dist/src/infra/stable-node-path.js +28 -0
- package/dist/src/infra/stable-node-path.js.map +1 -0
- package/dist/src/infra/update-global.d.ts +30 -23
- package/dist/src/infra/update-global.js +113 -64
- package/dist/src/infra/update-global.js.map +1 -1
- package/dist/src/infra/update-log.d.ts +1 -0
- package/dist/src/infra/update-log.js +12 -0
- package/dist/src/infra/update-log.js.map +1 -0
- package/dist/src/infra/update-restart.d.ts +20 -0
- package/dist/src/infra/update-restart.js +165 -0
- package/dist/src/infra/update-restart.js.map +1 -0
- package/dist/src/infra/update-runner.d.ts +89 -1
- package/dist/src/infra/update-runner.js +604 -173
- package/dist/src/infra/update-runner.js.map +1 -1
- package/dist/src/infra/update-startup.d.ts +3 -0
- package/dist/src/infra/update-startup.js +8 -4
- package/dist/src/infra/update-startup.js.map +1 -1
- package/dist/src/notes/attachment-ref.d.ts +9 -0
- package/dist/src/notes/attachment-ref.js +27 -0
- package/dist/src/notes/attachment-ref.js.map +1 -0
- package/dist/src/notes/index.d.ts +4 -0
- package/dist/src/notes/index.js +4 -0
- package/dist/src/notes/note-attachment-sync.d.ts +7 -0
- package/dist/src/notes/note-attachment-sync.js +46 -0
- package/dist/src/notes/note-attachment-sync.js.map +1 -0
- package/dist/src/notes/note-index-meta.d.ts +14 -0
- package/dist/src/notes/note-index-meta.js +87 -0
- package/dist/src/notes/note-index-meta.js.map +1 -0
- package/dist/src/notes/paths.d.ts +5 -0
- package/dist/src/notes/paths.js +23 -0
- package/dist/src/notes/paths.js.map +1 -0
- package/dist/src/notes/service.d.ts +42 -0
- package/dist/src/notes/service.js +331 -0
- package/dist/src/notes/service.js.map +1 -0
- package/dist/src/notes/store.d.ts +33 -0
- package/dist/src/notes/store.js +317 -0
- package/dist/src/notes/store.js.map +1 -0
- package/dist/src/notes/types.d.ts +162 -0
- package/dist/src/notes/types.js +1 -0
- package/dist/src/routing/resolve-route.d.ts +3 -1
- package/dist/src/routing/resolve-route.js.map +1 -1
- package/dist/src/session/store.d.ts +5 -3
- package/dist/src/session/store.js +66 -20
- package/dist/src/session/store.js.map +1 -1
- package/dist/src/utils/logger/stats.d.ts +1 -1
- package/dist/src/workflows/domain/event.d.ts +3 -0
- package/dist/src/workflows/domain/run.d.ts +3 -0
- package/dist/src/workflows/domain/run.js.map +1 -1
- package/dist/src/workflows/engine/projector.js +17 -0
- package/dist/src/workflows/engine/projector.js.map +1 -1
- package/dist/src/workflows/engine/workflow-engine.js +127 -0
- package/dist/src/workflows/engine/workflow-engine.js.map +1 -1
- package/dist/src/workflows/index.js +1 -1
- package/dist/src/workflows/service/run-view-to-snapshot.js +3 -1
- package/dist/src/workflows/service/run-view-to-snapshot.js.map +1 -1
- package/dist/src/workflows/service/workflow-run-service.d.ts +1 -0
- package/dist/src/workflows/service/workflow-run-service.js +4 -1
- package/dist/src/workflows/service/workflow-run-service.js.map +1 -1
- package/dist/src/workflows/service/workflow-session-bridge.js +1 -1
- package/package.json +1 -1
- package/dist/gateway/static/root/assets/agents-B6PJB07W.js +0 -222
- package/dist/gateway/static/root/assets/apps-page-BOr0B1wv.js +0 -1
- package/dist/gateway/static/root/assets/channels-settings-BelUKggl.js +0 -1
- package/dist/gateway/static/root/assets/cron-api-CjOg-BIj.js +0 -1
- package/dist/gateway/static/root/assets/cron-dreaming-jobs-DueM3rBz.js +0 -2
- package/dist/gateway/static/root/assets/cron-page-DhoZmZXb.js +0 -1
- package/dist/gateway/static/root/assets/dist-6LecgDx5.js +0 -1
- package/dist/gateway/static/root/assets/dist-BTWC-BTN.js +0 -45
- package/dist/gateway/static/root/assets/index-CiN1cQiQ.css +0 -1
- package/dist/gateway/static/root/assets/logs-page-BwWLfqvd.js +0 -1
- package/dist/gateway/static/root/assets/sessions-page-DV5WN8uk.js +0 -1
- package/dist/gateway/static/root/assets/settings-page-CfOBRbPX.js +0 -3
- package/dist/gateway/static/root/assets/share-preview-page-Di5Bzh4g.js +0 -2
- package/dist/gateway/static/root/assets/skills-page-D0H5Kaxg.js +0 -2
- package/dist/gateway/static/root/assets/url-aYn-Rj1C.js +0 -7
- package/dist/gateway/static/root/assets/vendor-codemirror-D0yxdRpg.js +0 -58
- package/dist/gateway/static/root/assets/voice-api-key-field-X2UfnHeq.js +0 -1
- package/dist/gateway/static/root/assets/workflows-page-BOPpO3NG.js +0 -27
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
//#region src/agent/workflow/builtins/competitor-scan.ts
|
|
2
|
+
/**
|
|
3
|
+
* Built-in workflow: `competitor_scan`
|
|
4
|
+
*
|
|
5
|
+
* Parallel competitor scan for solopreneurs — positioning, pricing, strengths,
|
|
6
|
+
* weaknesses, and differentiation opportunities.
|
|
7
|
+
*
|
|
8
|
+
* Args:
|
|
9
|
+
* - market: what you are building or selling
|
|
10
|
+
* - competitors: competitor names (optional)
|
|
11
|
+
* - focus: what you care most about (optional)
|
|
12
|
+
*/
|
|
13
|
+
const COMPETITOR_SCAN_SCRIPT = `export const meta = {
|
|
14
|
+
name: 'competitor_scan',
|
|
15
|
+
description: 'Scan competitors in parallel and synthesize positioning, pricing, and differentiation opportunities.',
|
|
16
|
+
whenToUse: 'Solo founder comparing alternatives before pricing, positioning, or go-to-market decisions.',
|
|
17
|
+
examplePrompts: [
|
|
18
|
+
{ field: 'market', text: 'AI writing assistant for solo creators' },
|
|
19
|
+
{ field: 'competitors', text: 'Notion AI, Jasper, Cursor' },
|
|
20
|
+
],
|
|
21
|
+
i18n: {
|
|
22
|
+
zh: {
|
|
23
|
+
description: '并行扫描竞品,综合定位、定价与差异化机会。',
|
|
24
|
+
whenToUse: '一人公司在定价、定位或进入市场前需要竞品对比时。',
|
|
25
|
+
examplePrompts: [
|
|
26
|
+
{ field: 'market', text: '面向独立创作者的 AI 写作助手' },
|
|
27
|
+
{ field: 'competitors', text: 'Notion AI、Jasper、Cursor' },
|
|
28
|
+
],
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
tags: ['research', 'investigation'],
|
|
32
|
+
estimatedAgents: { min: 4, max: 7 },
|
|
33
|
+
phases: [
|
|
34
|
+
{ title: 'Frame' },
|
|
35
|
+
{ title: 'Scan' },
|
|
36
|
+
{ title: 'Synthesize' },
|
|
37
|
+
],
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const RESEARCH_TOOLS = ['web_search', 'web_fetch']
|
|
41
|
+
|
|
42
|
+
const market = args && typeof args === 'object' && args.market
|
|
43
|
+
? String(args.market)
|
|
44
|
+
: 'Infer the market from the most recent user turn.'
|
|
45
|
+
|
|
46
|
+
const competitorsRaw = args && typeof args === 'object' && args.competitors
|
|
47
|
+
? String(args.competitors)
|
|
48
|
+
: ''
|
|
49
|
+
|
|
50
|
+
const focus = args && typeof args === 'object' && args.focus
|
|
51
|
+
? String(args.focus)
|
|
52
|
+
: 'positioning and pricing'
|
|
53
|
+
|
|
54
|
+
phase('Frame')
|
|
55
|
+
const frame = await agent(
|
|
56
|
+
'Frame this competitor scan. If competitors are not listed, propose 3–4 realistic competitors. Return scan criteria and your assumed buyer persona.\\n\\n' +
|
|
57
|
+
'MARKET:\\n' + market + '\\n' +
|
|
58
|
+
(competitorsRaw ? 'COMPETITORS:\\n' + competitorsRaw + '\\n' : '') +
|
|
59
|
+
'FOCUS:\\n' + focus,
|
|
60
|
+
{
|
|
61
|
+
label: 'frame',
|
|
62
|
+
toolset: RESEARCH_TOOLS,
|
|
63
|
+
schema: {
|
|
64
|
+
type: 'object',
|
|
65
|
+
properties: {
|
|
66
|
+
competitors: {
|
|
67
|
+
type: 'array',
|
|
68
|
+
items: {
|
|
69
|
+
type: 'object',
|
|
70
|
+
properties: {
|
|
71
|
+
name: { type: 'string' },
|
|
72
|
+
oneLiner: { type: 'string' },
|
|
73
|
+
},
|
|
74
|
+
required: ['name'],
|
|
75
|
+
},
|
|
76
|
+
},
|
|
77
|
+
criteria: { type: 'array', items: { type: 'string' } },
|
|
78
|
+
buyerPersona: { type: 'string' },
|
|
79
|
+
},
|
|
80
|
+
required: ['competitors', 'criteria'],
|
|
81
|
+
},
|
|
82
|
+
},
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
if (!frame || !frame.competitors?.length) {
|
|
86
|
+
return { ok: false, reason: 'framing failed', market }
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const competitors = frame.competitors.slice(0, 4)
|
|
90
|
+
|
|
91
|
+
phase('Scan')
|
|
92
|
+
const scans = await parallel(
|
|
93
|
+
competitors.map((c) => () =>
|
|
94
|
+
agent(
|
|
95
|
+
'Research this competitor for a solo founder. Use web search. Return positioning, pricing model, strengths, weaknesses, and target customer — cite sources where possible.\\n\\n' +
|
|
96
|
+
'COMPETITOR: ' + c.name + '\\nMARKET: ' + market + '\\nFOCUS: ' + focus,
|
|
97
|
+
{
|
|
98
|
+
label: c.name,
|
|
99
|
+
toolset: RESEARCH_TOOLS,
|
|
100
|
+
maxIterations: 25,
|
|
101
|
+
schema: {
|
|
102
|
+
type: 'object',
|
|
103
|
+
properties: {
|
|
104
|
+
name: { type: 'string' },
|
|
105
|
+
positioning: { type: 'string' },
|
|
106
|
+
pricing: { type: 'string' },
|
|
107
|
+
strengths: { type: 'array', items: { type: 'string' } },
|
|
108
|
+
weaknesses: { type: 'array', items: { type: 'string' } },
|
|
109
|
+
targetCustomer: { type: 'string' },
|
|
110
|
+
sources: { type: 'array', items: { type: 'string' } },
|
|
111
|
+
},
|
|
112
|
+
required: ['name', 'positioning', 'strengths', 'weaknesses'],
|
|
113
|
+
},
|
|
114
|
+
},
|
|
115
|
+
),
|
|
116
|
+
),
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
phase('Synthesize')
|
|
120
|
+
const synthesis = await agent(
|
|
121
|
+
'Synthesize a competitor matrix and differentiation playbook for a one-person company. Include whitespace opportunities, pricing band recommendation, and 3 positioning angles to test.\\n\\n' +
|
|
122
|
+
JSON.stringify({ market, focus, frame, scans: scans.filter(Boolean) }, null, 2),
|
|
123
|
+
{
|
|
124
|
+
label: 'synthesis',
|
|
125
|
+
schema: {
|
|
126
|
+
type: 'object',
|
|
127
|
+
properties: {
|
|
128
|
+
matrixSummary: { type: 'string' },
|
|
129
|
+
whitespace: { type: 'array', items: { type: 'string' } },
|
|
130
|
+
pricingBand: { type: 'string' },
|
|
131
|
+
positioningAngles: { type: 'array', items: { type: 'string' } },
|
|
132
|
+
avoidCompetingOn: { type: 'array', items: { type: 'string' } },
|
|
133
|
+
},
|
|
134
|
+
required: ['matrixSummary', 'whitespace', 'positioningAngles'],
|
|
135
|
+
},
|
|
136
|
+
},
|
|
137
|
+
)
|
|
138
|
+
|
|
139
|
+
return {
|
|
140
|
+
ok: true,
|
|
141
|
+
market,
|
|
142
|
+
competitorCount: competitors.length,
|
|
143
|
+
scans: scans.filter(Boolean),
|
|
144
|
+
...(synthesis ?? { matrixSummary: 'synthesis failed', whitespace: [], positioningAngles: [] }),
|
|
145
|
+
}
|
|
146
|
+
`;
|
|
147
|
+
//#endregion
|
|
148
|
+
export { COMPETITOR_SCAN_SCRIPT };
|
|
149
|
+
|
|
150
|
+
//# sourceMappingURL=competitor-scan.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"competitor-scan.js","names":[],"sources":["../../../../../src/agent/workflow/builtins/competitor-scan.ts"],"sourcesContent":["/**\n * Built-in workflow: `competitor_scan`\n *\n * Parallel competitor scan for solopreneurs — positioning, pricing, strengths,\n * weaknesses, and differentiation opportunities.\n *\n * Args:\n * - market: what you are building or selling\n * - competitors: competitor names (optional)\n * - focus: what you care most about (optional)\n */\n\nexport const COMPETITOR_SCAN_SCRIPT = `export const meta = {\n name: 'competitor_scan',\n description: 'Scan competitors in parallel and synthesize positioning, pricing, and differentiation opportunities.',\n whenToUse: 'Solo founder comparing alternatives before pricing, positioning, or go-to-market decisions.',\n examplePrompts: [\n { field: 'market', text: 'AI writing assistant for solo creators' },\n { field: 'competitors', text: 'Notion AI, Jasper, Cursor' },\n ],\n i18n: {\n zh: {\n description: '并行扫描竞品,综合定位、定价与差异化机会。',\n whenToUse: '一人公司在定价、定位或进入市场前需要竞品对比时。',\n examplePrompts: [\n { field: 'market', text: '面向独立创作者的 AI 写作助手' },\n { field: 'competitors', text: 'Notion AI、Jasper、Cursor' },\n ],\n },\n },\n tags: ['research', 'investigation'],\n estimatedAgents: { min: 4, max: 7 },\n phases: [\n { title: 'Frame' },\n { title: 'Scan' },\n { title: 'Synthesize' },\n ],\n}\n\nconst RESEARCH_TOOLS = ['web_search', 'web_fetch']\n\nconst market = args && typeof args === 'object' && args.market\n ? String(args.market)\n : 'Infer the market from the most recent user turn.'\n\nconst competitorsRaw = args && typeof args === 'object' && args.competitors\n ? String(args.competitors)\n : ''\n\nconst focus = args && typeof args === 'object' && args.focus\n ? String(args.focus)\n : 'positioning and pricing'\n\nphase('Frame')\nconst frame = await agent(\n 'Frame this competitor scan. If competitors are not listed, propose 3–4 realistic competitors. Return scan criteria and your assumed buyer persona.\\\\n\\\\n' +\n 'MARKET:\\\\n' + market + '\\\\n' +\n (competitorsRaw ? 'COMPETITORS:\\\\n' + competitorsRaw + '\\\\n' : '') +\n 'FOCUS:\\\\n' + focus,\n {\n label: 'frame',\n toolset: RESEARCH_TOOLS,\n schema: {\n type: 'object',\n properties: {\n competitors: {\n type: 'array',\n items: {\n type: 'object',\n properties: {\n name: { type: 'string' },\n oneLiner: { type: 'string' },\n },\n required: ['name'],\n },\n },\n criteria: { type: 'array', items: { type: 'string' } },\n buyerPersona: { type: 'string' },\n },\n required: ['competitors', 'criteria'],\n },\n },\n)\n\nif (!frame || !frame.competitors?.length) {\n return { ok: false, reason: 'framing failed', market }\n}\n\nconst competitors = frame.competitors.slice(0, 4)\n\nphase('Scan')\nconst scans = await parallel(\n competitors.map((c) => () =>\n agent(\n 'Research this competitor for a solo founder. Use web search. Return positioning, pricing model, strengths, weaknesses, and target customer — cite sources where possible.\\\\n\\\\n' +\n 'COMPETITOR: ' + c.name + '\\\\nMARKET: ' + market + '\\\\nFOCUS: ' + focus,\n {\n label: c.name,\n toolset: RESEARCH_TOOLS,\n maxIterations: 25,\n schema: {\n type: 'object',\n properties: {\n name: { type: 'string' },\n positioning: { type: 'string' },\n pricing: { type: 'string' },\n strengths: { type: 'array', items: { type: 'string' } },\n weaknesses: { type: 'array', items: { type: 'string' } },\n targetCustomer: { type: 'string' },\n sources: { type: 'array', items: { type: 'string' } },\n },\n required: ['name', 'positioning', 'strengths', 'weaknesses'],\n },\n },\n ),\n ),\n)\n\nphase('Synthesize')\nconst synthesis = await agent(\n 'Synthesize a competitor matrix and differentiation playbook for a one-person company. Include whitespace opportunities, pricing band recommendation, and 3 positioning angles to test.\\\\n\\\\n' +\n JSON.stringify({ market, focus, frame, scans: scans.filter(Boolean) }, null, 2),\n {\n label: 'synthesis',\n schema: {\n type: 'object',\n properties: {\n matrixSummary: { type: 'string' },\n whitespace: { type: 'array', items: { type: 'string' } },\n pricingBand: { type: 'string' },\n positioningAngles: { type: 'array', items: { type: 'string' } },\n avoidCompetingOn: { type: 'array', items: { type: 'string' } },\n },\n required: ['matrixSummary', 'whitespace', 'positioningAngles'],\n },\n },\n)\n\nreturn {\n ok: true,\n market,\n competitorCount: competitors.length,\n scans: scans.filter(Boolean),\n ...(synthesis ?? { matrixSummary: 'synthesis failed', whitespace: [], positioningAngles: [] }),\n}\n`\n"],"mappings":";;;;;;;;;;;;AAYA,MAAa,yBAAyB"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Built-in workflow: `content_draft`
|
|
3
|
+
*
|
|
4
|
+
* Multi-angle content drafting for non-code writing tasks — emails, posts,
|
|
5
|
+
* announcements, docs, or messages. Fans out tone/audience angles in parallel,
|
|
6
|
+
* then synthesizes a polished draft with variants.
|
|
7
|
+
*
|
|
8
|
+
* Args:
|
|
9
|
+
* - topic: what to write about
|
|
10
|
+
* - audience: who will read it (optional)
|
|
11
|
+
* - format: email | post | announcement | document | message (optional)
|
|
12
|
+
*/
|
|
13
|
+
export declare const CONTENT_DRAFT_SCRIPT = "export const meta = {\n name: 'content_draft',\n description: 'Draft polished content from multiple tone and audience angles, then synthesize the best version.',\n whenToUse: 'User needs help writing an email, post, announcement, doc section, or message \u2014 not code.',\n examplePrompts: [\n { field: 'topic', text: 'Write a product launch announcement for our mobile app update' },\n { field: 'topic', text: 'Draft a polite follow-up email after a missed meeting' },\n ],\n i18n: {\n zh: {\n description: '\u4ECE\u591A\u4E2A\u8BED\u6C14\u4E0E\u53D7\u4F17\u89D2\u5EA6\u8D77\u8349\u5185\u5BB9\uFF0C\u5E76\u7EFC\u5408\u4EA7\u51FA\u6700\u4F73\u7248\u672C\u3002',\n whenToUse: '\u7528\u6237\u9700\u8981\u5199\u90AE\u4EF6\u3001\u5E16\u5B50\u3001\u516C\u544A\u3001\u6587\u6863\u6BB5\u843D\u6216\u6D88\u606F\u7B49\u975E\u4EE3\u7801\u5185\u5BB9\u65F6\u3002',\n examplePrompts: [\n { field: 'topic', text: '\u5199\u4E00\u4EFD\u79FB\u52A8\u7AEF\u5E94\u7528\u66F4\u65B0\u7684\u4EA7\u54C1\u53D1\u5E03\u516C\u544A' },\n { field: 'topic', text: '\u8D77\u8349\u4E00\u5C01\u9519\u8FC7\u4F1A\u8BAE\u540E\u7684\u793C\u8C8C\u8DDF\u8FDB\u90AE\u4EF6' },\n ],\n },\n },\n tags: ['writing', 'content', 'communication'],\n estimatedAgents: { min: 4, max: 6 },\n phases: [\n { title: 'Brief' },\n { title: 'Angles' },\n { title: 'Draft' },\n ],\n}\n\nconst topic = args && typeof args === 'object' && args.topic\n ? String(args.topic)\n : 'Infer the writing topic from the most recent user turn.'\n\nconst audience = args && typeof args === 'object' && args.audience\n ? String(args.audience)\n : 'general professional audience'\n\nconst format = args && typeof args === 'object' && args.format\n ? String(args.format)\n : 'document'\n\nphase('Brief')\nconst brief = await agent(\n 'Clarify this writing brief. Return the core message, constraints, tone guidance, and 3 distinct angles worth exploring (each with a hook and key point).\\n\\n' +\n 'TOPIC:\\n' + topic + '\\nAUDIENCE:\\n' + audience + '\\nFORMAT:\\n' + format,\n {\n label: 'brief',\n schema: {\n type: 'object',\n properties: {\n coreMessage: { type: 'string' },\n tone: { type: 'string' },\n constraints: { type: 'array', items: { type: 'string' } },\n angles: {\n type: 'array',\n items: {\n type: 'object',\n properties: {\n title: { type: 'string' },\n hook: { type: 'string' },\n keyPoint: { type: 'string' },\n },\n required: ['title', 'hook', 'keyPoint'],\n },\n },\n },\n required: ['coreMessage', 'angles'],\n },\n },\n)\n\nif (!brief || !brief.angles?.length) {\n return { ok: false, reason: 'briefing failed', topic, audience, format }\n}\n\nconst angles = brief.angles.slice(0, 3)\n\nphase('Angles')\nconst angleDrafts = await parallel(\n angles.map((a) => () =>\n agent(\n 'Write a complete draft for this angle. Match the requested format and audience. Keep it ready to send or publish with minimal edits.\\n\\n' +\n 'FORMAT: ' + format + '\\nAUDIENCE: ' + audience + '\\nCORE MESSAGE: ' + brief.coreMessage + '\\n' +\n 'ANGLE: ' + a.title + '\\nHOOK: ' + a.hook + '\\nKEY POINT: ' + a.keyPoint,\n {\n label: a.title,\n schema: {\n type: 'object',\n properties: {\n title: { type: 'string' },\n body: { type: 'string' },\n subject: { type: 'string' },\n },\n required: ['title', 'body'],\n },\n },\n ),\n ),\n)\n\nphase('Draft')\nconst live = angleDrafts.filter(Boolean)\nconst finalDraft = await agent(\n 'Synthesize the best final draft from these angle-level versions. Pick the strongest hook and structure, merge the best lines, and remove redundancy. ' +\n 'Return the polished draft plus a one-line rationale and two optional shorter variants (e.g. social / SMS length).\\n\\n' +\n JSON.stringify({ brief, drafts: live }, null, 2),\n {\n label: 'final draft',\n schema: {\n type: 'object',\n properties: {\n title: { type: 'string' },\n subject: { type: 'string' },\n body: { type: 'string' },\n rationale: { type: 'string' },\n shortVariants: { type: 'array', items: { type: 'string' } },\n },\n required: ['body', 'rationale'],\n },\n },\n)\n\nreturn {\n ok: true,\n topic,\n audience,\n format,\n angleCount: angles.length,\n ...(finalDraft ?? { body: 'draft synthesis failed', rationale: '' }),\n}\n";
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
//#region src/agent/workflow/builtins/content-draft.ts
|
|
2
|
+
/**
|
|
3
|
+
* Built-in workflow: `content_draft`
|
|
4
|
+
*
|
|
5
|
+
* Multi-angle content drafting for non-code writing tasks — emails, posts,
|
|
6
|
+
* announcements, docs, or messages. Fans out tone/audience angles in parallel,
|
|
7
|
+
* then synthesizes a polished draft with variants.
|
|
8
|
+
*
|
|
9
|
+
* Args:
|
|
10
|
+
* - topic: what to write about
|
|
11
|
+
* - audience: who will read it (optional)
|
|
12
|
+
* - format: email | post | announcement | document | message (optional)
|
|
13
|
+
*/
|
|
14
|
+
const CONTENT_DRAFT_SCRIPT = `export const meta = {
|
|
15
|
+
name: 'content_draft',
|
|
16
|
+
description: 'Draft polished content from multiple tone and audience angles, then synthesize the best version.',
|
|
17
|
+
whenToUse: 'User needs help writing an email, post, announcement, doc section, or message — not code.',
|
|
18
|
+
examplePrompts: [
|
|
19
|
+
{ field: 'topic', text: 'Write a product launch announcement for our mobile app update' },
|
|
20
|
+
{ field: 'topic', text: 'Draft a polite follow-up email after a missed meeting' },
|
|
21
|
+
],
|
|
22
|
+
i18n: {
|
|
23
|
+
zh: {
|
|
24
|
+
description: '从多个语气与受众角度起草内容,并综合产出最佳版本。',
|
|
25
|
+
whenToUse: '用户需要写邮件、帖子、公告、文档段落或消息等非代码内容时。',
|
|
26
|
+
examplePrompts: [
|
|
27
|
+
{ field: 'topic', text: '写一份移动端应用更新的产品发布公告' },
|
|
28
|
+
{ field: 'topic', text: '起草一封错过会议后的礼貌跟进邮件' },
|
|
29
|
+
],
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
tags: ['writing', 'content', 'communication'],
|
|
33
|
+
estimatedAgents: { min: 4, max: 6 },
|
|
34
|
+
phases: [
|
|
35
|
+
{ title: 'Brief' },
|
|
36
|
+
{ title: 'Angles' },
|
|
37
|
+
{ title: 'Draft' },
|
|
38
|
+
],
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const topic = args && typeof args === 'object' && args.topic
|
|
42
|
+
? String(args.topic)
|
|
43
|
+
: 'Infer the writing topic from the most recent user turn.'
|
|
44
|
+
|
|
45
|
+
const audience = args && typeof args === 'object' && args.audience
|
|
46
|
+
? String(args.audience)
|
|
47
|
+
: 'general professional audience'
|
|
48
|
+
|
|
49
|
+
const format = args && typeof args === 'object' && args.format
|
|
50
|
+
? String(args.format)
|
|
51
|
+
: 'document'
|
|
52
|
+
|
|
53
|
+
phase('Brief')
|
|
54
|
+
const brief = await agent(
|
|
55
|
+
'Clarify this writing brief. Return the core message, constraints, tone guidance, and 3 distinct angles worth exploring (each with a hook and key point).\\n\\n' +
|
|
56
|
+
'TOPIC:\\n' + topic + '\\nAUDIENCE:\\n' + audience + '\\nFORMAT:\\n' + format,
|
|
57
|
+
{
|
|
58
|
+
label: 'brief',
|
|
59
|
+
schema: {
|
|
60
|
+
type: 'object',
|
|
61
|
+
properties: {
|
|
62
|
+
coreMessage: { type: 'string' },
|
|
63
|
+
tone: { type: 'string' },
|
|
64
|
+
constraints: { type: 'array', items: { type: 'string' } },
|
|
65
|
+
angles: {
|
|
66
|
+
type: 'array',
|
|
67
|
+
items: {
|
|
68
|
+
type: 'object',
|
|
69
|
+
properties: {
|
|
70
|
+
title: { type: 'string' },
|
|
71
|
+
hook: { type: 'string' },
|
|
72
|
+
keyPoint: { type: 'string' },
|
|
73
|
+
},
|
|
74
|
+
required: ['title', 'hook', 'keyPoint'],
|
|
75
|
+
},
|
|
76
|
+
},
|
|
77
|
+
},
|
|
78
|
+
required: ['coreMessage', 'angles'],
|
|
79
|
+
},
|
|
80
|
+
},
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
if (!brief || !brief.angles?.length) {
|
|
84
|
+
return { ok: false, reason: 'briefing failed', topic, audience, format }
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const angles = brief.angles.slice(0, 3)
|
|
88
|
+
|
|
89
|
+
phase('Angles')
|
|
90
|
+
const angleDrafts = await parallel(
|
|
91
|
+
angles.map((a) => () =>
|
|
92
|
+
agent(
|
|
93
|
+
'Write a complete draft for this angle. Match the requested format and audience. Keep it ready to send or publish with minimal edits.\\n\\n' +
|
|
94
|
+
'FORMAT: ' + format + '\\nAUDIENCE: ' + audience + '\\nCORE MESSAGE: ' + brief.coreMessage + '\\n' +
|
|
95
|
+
'ANGLE: ' + a.title + '\\nHOOK: ' + a.hook + '\\nKEY POINT: ' + a.keyPoint,
|
|
96
|
+
{
|
|
97
|
+
label: a.title,
|
|
98
|
+
schema: {
|
|
99
|
+
type: 'object',
|
|
100
|
+
properties: {
|
|
101
|
+
title: { type: 'string' },
|
|
102
|
+
body: { type: 'string' },
|
|
103
|
+
subject: { type: 'string' },
|
|
104
|
+
},
|
|
105
|
+
required: ['title', 'body'],
|
|
106
|
+
},
|
|
107
|
+
},
|
|
108
|
+
),
|
|
109
|
+
),
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
phase('Draft')
|
|
113
|
+
const live = angleDrafts.filter(Boolean)
|
|
114
|
+
const finalDraft = await agent(
|
|
115
|
+
'Synthesize the best final draft from these angle-level versions. Pick the strongest hook and structure, merge the best lines, and remove redundancy. ' +
|
|
116
|
+
'Return the polished draft plus a one-line rationale and two optional shorter variants (e.g. social / SMS length).\\n\\n' +
|
|
117
|
+
JSON.stringify({ brief, drafts: live }, null, 2),
|
|
118
|
+
{
|
|
119
|
+
label: 'final draft',
|
|
120
|
+
schema: {
|
|
121
|
+
type: 'object',
|
|
122
|
+
properties: {
|
|
123
|
+
title: { type: 'string' },
|
|
124
|
+
subject: { type: 'string' },
|
|
125
|
+
body: { type: 'string' },
|
|
126
|
+
rationale: { type: 'string' },
|
|
127
|
+
shortVariants: { type: 'array', items: { type: 'string' } },
|
|
128
|
+
},
|
|
129
|
+
required: ['body', 'rationale'],
|
|
130
|
+
},
|
|
131
|
+
},
|
|
132
|
+
)
|
|
133
|
+
|
|
134
|
+
return {
|
|
135
|
+
ok: true,
|
|
136
|
+
topic,
|
|
137
|
+
audience,
|
|
138
|
+
format,
|
|
139
|
+
angleCount: angles.length,
|
|
140
|
+
...(finalDraft ?? { body: 'draft synthesis failed', rationale: '' }),
|
|
141
|
+
}
|
|
142
|
+
`;
|
|
143
|
+
//#endregion
|
|
144
|
+
export { CONTENT_DRAFT_SCRIPT };
|
|
145
|
+
|
|
146
|
+
//# sourceMappingURL=content-draft.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"content-draft.js","names":[],"sources":["../../../../../src/agent/workflow/builtins/content-draft.ts"],"sourcesContent":["/**\n * Built-in workflow: `content_draft`\n *\n * Multi-angle content drafting for non-code writing tasks — emails, posts,\n * announcements, docs, or messages. Fans out tone/audience angles in parallel,\n * then synthesizes a polished draft with variants.\n *\n * Args:\n * - topic: what to write about\n * - audience: who will read it (optional)\n * - format: email | post | announcement | document | message (optional)\n */\n\nexport const CONTENT_DRAFT_SCRIPT = `export const meta = {\n name: 'content_draft',\n description: 'Draft polished content from multiple tone and audience angles, then synthesize the best version.',\n whenToUse: 'User needs help writing an email, post, announcement, doc section, or message — not code.',\n examplePrompts: [\n { field: 'topic', text: 'Write a product launch announcement for our mobile app update' },\n { field: 'topic', text: 'Draft a polite follow-up email after a missed meeting' },\n ],\n i18n: {\n zh: {\n description: '从多个语气与受众角度起草内容,并综合产出最佳版本。',\n whenToUse: '用户需要写邮件、帖子、公告、文档段落或消息等非代码内容时。',\n examplePrompts: [\n { field: 'topic', text: '写一份移动端应用更新的产品发布公告' },\n { field: 'topic', text: '起草一封错过会议后的礼貌跟进邮件' },\n ],\n },\n },\n tags: ['writing', 'content', 'communication'],\n estimatedAgents: { min: 4, max: 6 },\n phases: [\n { title: 'Brief' },\n { title: 'Angles' },\n { title: 'Draft' },\n ],\n}\n\nconst topic = args && typeof args === 'object' && args.topic\n ? String(args.topic)\n : 'Infer the writing topic from the most recent user turn.'\n\nconst audience = args && typeof args === 'object' && args.audience\n ? String(args.audience)\n : 'general professional audience'\n\nconst format = args && typeof args === 'object' && args.format\n ? String(args.format)\n : 'document'\n\nphase('Brief')\nconst brief = await agent(\n 'Clarify this writing brief. Return the core message, constraints, tone guidance, and 3 distinct angles worth exploring (each with a hook and key point).\\\\n\\\\n' +\n 'TOPIC:\\\\n' + topic + '\\\\nAUDIENCE:\\\\n' + audience + '\\\\nFORMAT:\\\\n' + format,\n {\n label: 'brief',\n schema: {\n type: 'object',\n properties: {\n coreMessage: { type: 'string' },\n tone: { type: 'string' },\n constraints: { type: 'array', items: { type: 'string' } },\n angles: {\n type: 'array',\n items: {\n type: 'object',\n properties: {\n title: { type: 'string' },\n hook: { type: 'string' },\n keyPoint: { type: 'string' },\n },\n required: ['title', 'hook', 'keyPoint'],\n },\n },\n },\n required: ['coreMessage', 'angles'],\n },\n },\n)\n\nif (!brief || !brief.angles?.length) {\n return { ok: false, reason: 'briefing failed', topic, audience, format }\n}\n\nconst angles = brief.angles.slice(0, 3)\n\nphase('Angles')\nconst angleDrafts = await parallel(\n angles.map((a) => () =>\n agent(\n 'Write a complete draft for this angle. Match the requested format and audience. Keep it ready to send or publish with minimal edits.\\\\n\\\\n' +\n 'FORMAT: ' + format + '\\\\nAUDIENCE: ' + audience + '\\\\nCORE MESSAGE: ' + brief.coreMessage + '\\\\n' +\n 'ANGLE: ' + a.title + '\\\\nHOOK: ' + a.hook + '\\\\nKEY POINT: ' + a.keyPoint,\n {\n label: a.title,\n schema: {\n type: 'object',\n properties: {\n title: { type: 'string' },\n body: { type: 'string' },\n subject: { type: 'string' },\n },\n required: ['title', 'body'],\n },\n },\n ),\n ),\n)\n\nphase('Draft')\nconst live = angleDrafts.filter(Boolean)\nconst finalDraft = await agent(\n 'Synthesize the best final draft from these angle-level versions. Pick the strongest hook and structure, merge the best lines, and remove redundancy. ' +\n 'Return the polished draft plus a one-line rationale and two optional shorter variants (e.g. social / SMS length).\\\\n\\\\n' +\n JSON.stringify({ brief, drafts: live }, null, 2),\n {\n label: 'final draft',\n schema: {\n type: 'object',\n properties: {\n title: { type: 'string' },\n subject: { type: 'string' },\n body: { type: 'string' },\n rationale: { type: 'string' },\n shortVariants: { type: 'array', items: { type: 'string' } },\n },\n required: ['body', 'rationale'],\n },\n },\n)\n\nreturn {\n ok: true,\n topic,\n audience,\n format,\n angleCount: angles.length,\n ...(finalDraft ?? { body: 'draft synthesis failed', rationale: '' }),\n}\n`\n"],"mappings":";;;;;;;;;;;;;AAaA,MAAa,uBAAuB"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Built-in workflow: `content_repurpose`
|
|
3
|
+
*
|
|
4
|
+
* Repurpose one source piece into multi-platform formats — threads, LinkedIn,
|
|
5
|
+
* short video script, newsletter blurb. For personal-brand solopreneurs.
|
|
6
|
+
*
|
|
7
|
+
* Args:
|
|
8
|
+
* - source: original article, notes, or talking points
|
|
9
|
+
* - platforms: target platforms (optional)
|
|
10
|
+
*/
|
|
11
|
+
export declare const CONTENT_REPURPOSE_SCRIPT = "export const meta = {\n name: 'content_repurpose',\n description: 'Repurpose one source into platform-specific content (threads, posts, scripts, newsletter).',\n whenToUse: 'User has existing content and wants multi-platform distribution without rewriting from scratch.',\n examplePrompts: [\n { field: 'source', text: '2000-word blog post on building a one-person AI business' },\n { field: 'platforms', text: 'X thread, LinkedIn, 60s video script, newsletter teaser' },\n ],\n i18n: {\n zh: {\n description: '\u5C06\u4E00\u4EFD\u6838\u5FC3\u5185\u5BB9\u6539\u7F16\u4E3A\u591A\u5E73\u53F0\u683C\u5F0F\uFF08\u63A8\u6587\u4E32\u3001\u9886\u82F1\u5E16\u3001\u77ED\u89C6\u9891\u811A\u672C\u3001Newsletter \u6458\u8981\uFF09\u3002',\n whenToUse: '\u7528\u6237\u5DF2\u6709\u5185\u5BB9\u7D20\u6750\uFF0C\u9700\u8981\u4E00\u6E90\u591A\u7528\u3001\u591A\u5E73\u53F0\u5206\u53D1\u65F6\u3002',\n examplePrompts: [\n { field: 'source', text: '\u4E00\u7BC7 2000 \u5B57\u5173\u4E8E\u4E00\u4EBA AI \u516C\u53F8\u7684\u535A\u5BA2' },\n { field: 'platforms', text: 'X \u63A8\u6587\u4E32\u3001LinkedIn\u300160 \u79D2\u53E3\u64AD\u7A3F\u3001Newsletter \u5BFC\u8BED' },\n ],\n },\n },\n tags: ['writing', 'content'],\n estimatedAgents: { min: 4, max: 6 },\n phases: [\n { title: 'Extract' },\n { title: 'Adapt' },\n { title: 'Package' },\n ],\n}\n\nconst source = args && typeof args === 'object' && args.source\n ? String(args.source)\n : 'Infer the source content from the most recent user turn.'\n\nconst platformsRaw = args && typeof args === 'object' && args.platforms\n ? String(args.platforms)\n : ''\n\nconst DEFAULT_PLATFORMS = [\n { id: 'x_thread', label: 'X / Twitter thread', format: '5\u20138 tweets, hook-first, one idea per tweet' },\n { id: 'linkedin', label: 'LinkedIn post', format: 'Professional post, 150\u2013250 words, clear CTA' },\n { id: 'short_video', label: 'Short video script', format: '60-second spoken script with hook and CTA' },\n { id: 'newsletter', label: 'Newsletter teaser', format: 'Subject line + 2\u20133 sentence teaser + bullet highlights' },\n]\n\nphase('Extract')\nconst extracted = await agent(\n 'Extract reusable content atoms from this source: core thesis, 3\u20135 key points, best quotes/stats, audience hook, and CTA.\\n\\nSOURCE:\\n' + source,\n {\n label: 'extract',\n schema: {\n type: 'object',\n properties: {\n thesis: { type: 'string' },\n keyPoints: { type: 'array', items: { type: 'string' } },\n hooks: { type: 'array', items: { type: 'string' } },\n quotesOrStats: { type: 'array', items: { type: 'string' } },\n cta: { type: 'string' },\n },\n required: ['thesis', 'keyPoints', 'cta'],\n },\n },\n)\n\nconst targets = platformsRaw\n ? platformsRaw\n .split(/[,\uFF0C\\n]/)\n .map((s) => s.trim())\n .filter(Boolean)\n .map((label, index) => ({\n id: 'custom_' + index,\n label,\n format: 'Native format and length for ' + label,\n }))\n : DEFAULT_PLATFORMS\n\nphase('Adapt')\nconst adaptations = await parallel(\n targets.map((p) => () =>\n agent(\n 'Adapt the extracted content for this platform. Match native tone and length. Return ready-to-publish copy.\\n\\n' +\n 'PLATFORM: ' + p.label + '\\nFORMAT: ' + p.format + '\\n\\n' +\n JSON.stringify(extracted, null, 2),\n {\n label: p.label,\n schema: {\n type: 'object',\n properties: {\n platform: { type: 'string' },\n copy: { type: 'string' },\n notes: { type: 'string' },\n },\n required: ['platform', 'copy'],\n },\n },\n ),\n ),\n)\n\nphase('Package')\nconst packaged = await agent(\n 'Package all platform adaptations with a publishing checklist: suggested order, cross-links between posts, and one optional bonus format (e.g. carousel outline).\\n\\n' +\n JSON.stringify({ extracted, adaptations: adaptations.filter(Boolean) }, null, 2),\n {\n label: 'package',\n schema: {\n type: 'object',\n properties: {\n publishingOrder: { type: 'array', items: { type: 'string' } },\n crossLinks: { type: 'array', items: { type: 'string' } },\n bonusFormat: { type: 'string' },\n summary: { type: 'string' },\n },\n required: ['publishingOrder', 'summary'],\n },\n },\n)\n\nreturn {\n ok: true,\n extracted,\n adaptations: adaptations.filter(Boolean),\n ...(packaged ?? { publishingOrder: [], summary: 'packaging failed' }),\n}\n";
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
//#region src/agent/workflow/builtins/content-repurpose.ts
|
|
2
|
+
/**
|
|
3
|
+
* Built-in workflow: `content_repurpose`
|
|
4
|
+
*
|
|
5
|
+
* Repurpose one source piece into multi-platform formats — threads, LinkedIn,
|
|
6
|
+
* short video script, newsletter blurb. For personal-brand solopreneurs.
|
|
7
|
+
*
|
|
8
|
+
* Args:
|
|
9
|
+
* - source: original article, notes, or talking points
|
|
10
|
+
* - platforms: target platforms (optional)
|
|
11
|
+
*/
|
|
12
|
+
const CONTENT_REPURPOSE_SCRIPT = `export const meta = {
|
|
13
|
+
name: 'content_repurpose',
|
|
14
|
+
description: 'Repurpose one source into platform-specific content (threads, posts, scripts, newsletter).',
|
|
15
|
+
whenToUse: 'User has existing content and wants multi-platform distribution without rewriting from scratch.',
|
|
16
|
+
examplePrompts: [
|
|
17
|
+
{ field: 'source', text: '2000-word blog post on building a one-person AI business' },
|
|
18
|
+
{ field: 'platforms', text: 'X thread, LinkedIn, 60s video script, newsletter teaser' },
|
|
19
|
+
],
|
|
20
|
+
i18n: {
|
|
21
|
+
zh: {
|
|
22
|
+
description: '将一份核心内容改编为多平台格式(推文串、领英帖、短视频脚本、Newsletter 摘要)。',
|
|
23
|
+
whenToUse: '用户已有内容素材,需要一源多用、多平台分发时。',
|
|
24
|
+
examplePrompts: [
|
|
25
|
+
{ field: 'source', text: '一篇 2000 字关于一人 AI 公司的博客' },
|
|
26
|
+
{ field: 'platforms', text: 'X 推文串、LinkedIn、60 秒口播稿、Newsletter 导语' },
|
|
27
|
+
],
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
tags: ['writing', 'content'],
|
|
31
|
+
estimatedAgents: { min: 4, max: 6 },
|
|
32
|
+
phases: [
|
|
33
|
+
{ title: 'Extract' },
|
|
34
|
+
{ title: 'Adapt' },
|
|
35
|
+
{ title: 'Package' },
|
|
36
|
+
],
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const source = args && typeof args === 'object' && args.source
|
|
40
|
+
? String(args.source)
|
|
41
|
+
: 'Infer the source content from the most recent user turn.'
|
|
42
|
+
|
|
43
|
+
const platformsRaw = args && typeof args === 'object' && args.platforms
|
|
44
|
+
? String(args.platforms)
|
|
45
|
+
: ''
|
|
46
|
+
|
|
47
|
+
const DEFAULT_PLATFORMS = [
|
|
48
|
+
{ id: 'x_thread', label: 'X / Twitter thread', format: '5–8 tweets, hook-first, one idea per tweet' },
|
|
49
|
+
{ id: 'linkedin', label: 'LinkedIn post', format: 'Professional post, 150–250 words, clear CTA' },
|
|
50
|
+
{ id: 'short_video', label: 'Short video script', format: '60-second spoken script with hook and CTA' },
|
|
51
|
+
{ id: 'newsletter', label: 'Newsletter teaser', format: 'Subject line + 2–3 sentence teaser + bullet highlights' },
|
|
52
|
+
]
|
|
53
|
+
|
|
54
|
+
phase('Extract')
|
|
55
|
+
const extracted = await agent(
|
|
56
|
+
'Extract reusable content atoms from this source: core thesis, 3–5 key points, best quotes/stats, audience hook, and CTA.\\n\\nSOURCE:\\n' + source,
|
|
57
|
+
{
|
|
58
|
+
label: 'extract',
|
|
59
|
+
schema: {
|
|
60
|
+
type: 'object',
|
|
61
|
+
properties: {
|
|
62
|
+
thesis: { type: 'string' },
|
|
63
|
+
keyPoints: { type: 'array', items: { type: 'string' } },
|
|
64
|
+
hooks: { type: 'array', items: { type: 'string' } },
|
|
65
|
+
quotesOrStats: { type: 'array', items: { type: 'string' } },
|
|
66
|
+
cta: { type: 'string' },
|
|
67
|
+
},
|
|
68
|
+
required: ['thesis', 'keyPoints', 'cta'],
|
|
69
|
+
},
|
|
70
|
+
},
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
const targets = platformsRaw
|
|
74
|
+
? platformsRaw
|
|
75
|
+
.split(/[,,\\n]/)
|
|
76
|
+
.map((s) => s.trim())
|
|
77
|
+
.filter(Boolean)
|
|
78
|
+
.map((label, index) => ({
|
|
79
|
+
id: 'custom_' + index,
|
|
80
|
+
label,
|
|
81
|
+
format: 'Native format and length for ' + label,
|
|
82
|
+
}))
|
|
83
|
+
: DEFAULT_PLATFORMS
|
|
84
|
+
|
|
85
|
+
phase('Adapt')
|
|
86
|
+
const adaptations = await parallel(
|
|
87
|
+
targets.map((p) => () =>
|
|
88
|
+
agent(
|
|
89
|
+
'Adapt the extracted content for this platform. Match native tone and length. Return ready-to-publish copy.\\n\\n' +
|
|
90
|
+
'PLATFORM: ' + p.label + '\\nFORMAT: ' + p.format + '\\n\\n' +
|
|
91
|
+
JSON.stringify(extracted, null, 2),
|
|
92
|
+
{
|
|
93
|
+
label: p.label,
|
|
94
|
+
schema: {
|
|
95
|
+
type: 'object',
|
|
96
|
+
properties: {
|
|
97
|
+
platform: { type: 'string' },
|
|
98
|
+
copy: { type: 'string' },
|
|
99
|
+
notes: { type: 'string' },
|
|
100
|
+
},
|
|
101
|
+
required: ['platform', 'copy'],
|
|
102
|
+
},
|
|
103
|
+
},
|
|
104
|
+
),
|
|
105
|
+
),
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
phase('Package')
|
|
109
|
+
const packaged = await agent(
|
|
110
|
+
'Package all platform adaptations with a publishing checklist: suggested order, cross-links between posts, and one optional bonus format (e.g. carousel outline).\\n\\n' +
|
|
111
|
+
JSON.stringify({ extracted, adaptations: adaptations.filter(Boolean) }, null, 2),
|
|
112
|
+
{
|
|
113
|
+
label: 'package',
|
|
114
|
+
schema: {
|
|
115
|
+
type: 'object',
|
|
116
|
+
properties: {
|
|
117
|
+
publishingOrder: { type: 'array', items: { type: 'string' } },
|
|
118
|
+
crossLinks: { type: 'array', items: { type: 'string' } },
|
|
119
|
+
bonusFormat: { type: 'string' },
|
|
120
|
+
summary: { type: 'string' },
|
|
121
|
+
},
|
|
122
|
+
required: ['publishingOrder', 'summary'],
|
|
123
|
+
},
|
|
124
|
+
},
|
|
125
|
+
)
|
|
126
|
+
|
|
127
|
+
return {
|
|
128
|
+
ok: true,
|
|
129
|
+
extracted,
|
|
130
|
+
adaptations: adaptations.filter(Boolean),
|
|
131
|
+
...(packaged ?? { publishingOrder: [], summary: 'packaging failed' }),
|
|
132
|
+
}
|
|
133
|
+
`;
|
|
134
|
+
//#endregion
|
|
135
|
+
export { CONTENT_REPURPOSE_SCRIPT };
|
|
136
|
+
|
|
137
|
+
//# sourceMappingURL=content-repurpose.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"content-repurpose.js","names":[],"sources":["../../../../../src/agent/workflow/builtins/content-repurpose.ts"],"sourcesContent":["/**\n * Built-in workflow: `content_repurpose`\n *\n * Repurpose one source piece into multi-platform formats — threads, LinkedIn,\n * short video script, newsletter blurb. For personal-brand solopreneurs.\n *\n * Args:\n * - source: original article, notes, or talking points\n * - platforms: target platforms (optional)\n */\n\nexport const CONTENT_REPURPOSE_SCRIPT = `export const meta = {\n name: 'content_repurpose',\n description: 'Repurpose one source into platform-specific content (threads, posts, scripts, newsletter).',\n whenToUse: 'User has existing content and wants multi-platform distribution without rewriting from scratch.',\n examplePrompts: [\n { field: 'source', text: '2000-word blog post on building a one-person AI business' },\n { field: 'platforms', text: 'X thread, LinkedIn, 60s video script, newsletter teaser' },\n ],\n i18n: {\n zh: {\n description: '将一份核心内容改编为多平台格式(推文串、领英帖、短视频脚本、Newsletter 摘要)。',\n whenToUse: '用户已有内容素材,需要一源多用、多平台分发时。',\n examplePrompts: [\n { field: 'source', text: '一篇 2000 字关于一人 AI 公司的博客' },\n { field: 'platforms', text: 'X 推文串、LinkedIn、60 秒口播稿、Newsletter 导语' },\n ],\n },\n },\n tags: ['writing', 'content'],\n estimatedAgents: { min: 4, max: 6 },\n phases: [\n { title: 'Extract' },\n { title: 'Adapt' },\n { title: 'Package' },\n ],\n}\n\nconst source = args && typeof args === 'object' && args.source\n ? String(args.source)\n : 'Infer the source content from the most recent user turn.'\n\nconst platformsRaw = args && typeof args === 'object' && args.platforms\n ? String(args.platforms)\n : ''\n\nconst DEFAULT_PLATFORMS = [\n { id: 'x_thread', label: 'X / Twitter thread', format: '5–8 tweets, hook-first, one idea per tweet' },\n { id: 'linkedin', label: 'LinkedIn post', format: 'Professional post, 150–250 words, clear CTA' },\n { id: 'short_video', label: 'Short video script', format: '60-second spoken script with hook and CTA' },\n { id: 'newsletter', label: 'Newsletter teaser', format: 'Subject line + 2–3 sentence teaser + bullet highlights' },\n]\n\nphase('Extract')\nconst extracted = await agent(\n 'Extract reusable content atoms from this source: core thesis, 3–5 key points, best quotes/stats, audience hook, and CTA.\\\\n\\\\nSOURCE:\\\\n' + source,\n {\n label: 'extract',\n schema: {\n type: 'object',\n properties: {\n thesis: { type: 'string' },\n keyPoints: { type: 'array', items: { type: 'string' } },\n hooks: { type: 'array', items: { type: 'string' } },\n quotesOrStats: { type: 'array', items: { type: 'string' } },\n cta: { type: 'string' },\n },\n required: ['thesis', 'keyPoints', 'cta'],\n },\n },\n)\n\nconst targets = platformsRaw\n ? platformsRaw\n .split(/[,,\\\\n]/)\n .map((s) => s.trim())\n .filter(Boolean)\n .map((label, index) => ({\n id: 'custom_' + index,\n label,\n format: 'Native format and length for ' + label,\n }))\n : DEFAULT_PLATFORMS\n\nphase('Adapt')\nconst adaptations = await parallel(\n targets.map((p) => () =>\n agent(\n 'Adapt the extracted content for this platform. Match native tone and length. Return ready-to-publish copy.\\\\n\\\\n' +\n 'PLATFORM: ' + p.label + '\\\\nFORMAT: ' + p.format + '\\\\n\\\\n' +\n JSON.stringify(extracted, null, 2),\n {\n label: p.label,\n schema: {\n type: 'object',\n properties: {\n platform: { type: 'string' },\n copy: { type: 'string' },\n notes: { type: 'string' },\n },\n required: ['platform', 'copy'],\n },\n },\n ),\n ),\n)\n\nphase('Package')\nconst packaged = await agent(\n 'Package all platform adaptations with a publishing checklist: suggested order, cross-links between posts, and one optional bonus format (e.g. carousel outline).\\\\n\\\\n' +\n JSON.stringify({ extracted, adaptations: adaptations.filter(Boolean) }, null, 2),\n {\n label: 'package',\n schema: {\n type: 'object',\n properties: {\n publishingOrder: { type: 'array', items: { type: 'string' } },\n crossLinks: { type: 'array', items: { type: 'string' } },\n bonusFormat: { type: 'string' },\n summary: { type: 'string' },\n },\n required: ['publishingOrder', 'summary'],\n },\n },\n)\n\nreturn {\n ok: true,\n extracted,\n adaptations: adaptations.filter(Boolean),\n ...(packaged ?? { publishingOrder: [], summary: 'packaging failed' }),\n}\n`\n"],"mappings":";;;;;;;;;;;AAWA,MAAa,2BAA2B"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Built-in workflow: `decision_compare`
|
|
3
|
+
*
|
|
4
|
+
* Structured comparison of options for everyday decisions — vendors, approaches,
|
|
5
|
+
* purchases, policies, or life choices. Independent evaluators score each option,
|
|
6
|
+
* then a synthesizer recommends with trade-offs spelled out.
|
|
7
|
+
*
|
|
8
|
+
* Args:
|
|
9
|
+
* - question: the decision to make
|
|
10
|
+
* - options: optional comma-separated or newline-separated option list
|
|
11
|
+
* - criteria: optional evaluation criteria hint
|
|
12
|
+
*/
|
|
13
|
+
export declare const DECISION_COMPARE_SCRIPT = "export const meta = {\n name: 'decision_compare',\n description: 'Compare options across independent evaluators, then recommend with explicit trade-offs.',\n whenToUse: 'User faces a non-code decision with multiple viable options and wants a structured comparison.',\n examplePrompts: [\n { field: 'question', text: 'Which project management tool fits a 5-person remote team?' },\n { field: 'question', text: 'Should we host the event in-person, hybrid, or fully virtual?' },\n ],\n i18n: {\n zh: {\n description: '\u7531\u591A\u4E2A\u72EC\u7ACB\u8BC4\u5BA1\u7EF4\u5EA6\u5BF9\u6BD4\u9009\u9879\uFF0C\u5E76\u7ED9\u51FA\u542B\u6743\u8861\u7684\u63A8\u8350\u7ED3\u8BBA\u3002',\n whenToUse: '\u7528\u6237\u9762\u4E34\u591A\u4E2A\u53EF\u884C\u65B9\u6848\u7684\u975E\u4EE3\u7801\u51B3\u7B56\uFF0C\u9700\u8981\u7ED3\u6784\u5316\u5BF9\u6BD4\u65F6\u3002',\n examplePrompts: [\n { field: 'question', text: '5 \u4EBA\u8FDC\u7A0B\u56E2\u961F\u9002\u5408\u7528\u54EA\u6B3E\u9879\u76EE\u7BA1\u7406\u5DE5\u5177\uFF1F' },\n { field: 'question', text: '\u6D3B\u52A8\u5E94\u8BE5\u7EBF\u4E0B\u3001\u6DF7\u5408\u8FD8\u662F\u7EAF\u7EBF\u4E0A\u4E3E\u529E\uFF1F' },\n ],\n },\n },\n tags: ['decision-making', 'comparison', 'productivity'],\n estimatedAgents: { min: 4, max: 7 },\n phases: [\n { title: 'Frame' },\n { title: 'Evaluate' },\n { title: 'Recommend' },\n ],\n}\n\nconst question = args && typeof args === 'object' && args.question\n ? String(args.question)\n : 'Infer the decision question from the most recent user turn.'\n\nconst optionsRaw = args && typeof args === 'object' && args.options\n ? String(args.options)\n : ''\n\nconst criteriaHint = args && typeof args === 'object' && args.criteria\n ? String(args.criteria)\n : ''\n\nphase('Frame')\nconst frame = await agent(\n 'Frame this decision. If options are not provided, propose 3\u20134 realistic options. Return evaluation criteria (weighted if helpful), assumptions, and non-goals.\\n\\n' +\n 'QUESTION:\\n' + question + '\\n' +\n (optionsRaw ? 'OPTIONS:\\n' + optionsRaw + '\\n' : '') +\n (criteriaHint ? 'CRITERIA HINT:\\n' + criteriaHint + '\\n' : ''),\n {\n label: 'framing',\n schema: {\n type: 'object',\n properties: {\n options: {\n type: 'array',\n items: {\n type: 'object',\n properties: {\n name: { type: 'string' },\n summary: { type: 'string' },\n },\n required: ['name', 'summary'],\n },\n },\n criteria: {\n type: 'array',\n items: {\n type: 'object',\n properties: {\n name: { type: 'string' },\n weight: { type: 'string', enum: ['low', 'med', 'high'] },\n },\n required: ['name'],\n },\n },\n assumptions: { type: 'array', items: { type: 'string' } },\n },\n required: ['options', 'criteria'],\n },\n },\n)\n\nif (!frame || !frame.options?.length) {\n return { ok: false, reason: 'framing failed', question }\n}\n\nconst options = frame.options.slice(0, 4)\nconst criteria = frame.criteria ?? []\n\nconst LENSES = [\n { name: 'Benefits', focus: 'Upside, value delivered, and who wins.' },\n { name: 'Risks', focus: 'Downside, failure modes, hidden costs, and regrets.' },\n { name: 'Fit', focus: 'Fit for stated constraints, audience, timeline, and resources.' },\n]\n\nphase('Evaluate')\nconst evaluations = await parallel(\n LENSES.map((lens) => () =>\n agent(\n 'Evaluate every option through this lens. Be specific \u2014 avoid generic pros/cons. Score each option low/med/high for this lens with a one-line rationale.\\n\\n' +\n 'LENS: ' + lens.name + ' \u2014 ' + lens.focus + '\\n' +\n 'QUESTION: ' + question + '\\n' +\n 'OPTIONS:\\n' + JSON.stringify(options, null, 2) + '\\n' +\n 'CRITERIA:\\n' + JSON.stringify(criteria, null, 2),\n {\n label: lens.name,\n schema: {\n type: 'object',\n properties: {\n lens: { type: 'string' },\n scores: {\n type: 'array',\n items: {\n type: 'object',\n properties: {\n option: { type: 'string' },\n score: { type: 'string', enum: ['low', 'med', 'high'] },\n rationale: { type: 'string' },\n },\n required: ['option', 'score', 'rationale'],\n },\n },\n },\n required: ['lens', 'scores'],\n },\n },\n ),\n ),\n)\n\nphase('Recommend')\nconst live = evaluations.filter(Boolean)\nconst recommendation = await agent(\n 'Recommend the best option (or a hybrid) from these lens-level evaluations. State trade-offs explicitly, note what would change the recommendation, and give a runner-up.\\n\\n' +\n 'QUESTION:\\n' + question + '\\n\\n' +\n JSON.stringify({ frame, evaluations: live }, null, 2),\n {\n label: 'recommendation',\n schema: {\n type: 'object',\n properties: {\n recommendation: { type: 'string' },\n rationale: { type: 'string' },\n tradeoffs: { type: 'array', items: { type: 'string' } },\n runnerUp: { type: 'string' },\n whatWouldChange: { type: 'array', items: { type: 'string' } },\n },\n required: ['recommendation', 'rationale', 'tradeoffs'],\n },\n },\n)\n\nreturn {\n ok: true,\n question,\n optionCount: options.length,\n ...(recommendation ?? { recommendation: 'recommendation failed', rationale: '', tradeoffs: [] }),\n}\n";
|