@synap-core/workspace-templates 0.12.0 → 0.24.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +509 -0
- package/automations/bookmark-enrichment.automation.json +321 -0
- package/automations/github-pr-events.automation.json +505 -0
- package/automations/mail-feed.automation.json +86 -0
- package/automations/ship-a-change-checks-green.automation.json +106 -0
- package/automations/ship-a-change-checks-red.automation.json +106 -0
- package/automations/ship-a-change-deployed.automation.json +56 -0
- package/automations/ship-a-change-pr-merged.automation.json +110 -0
- package/automations/ship-a-change-pr-opened.automation.json +104 -0
- package/automations/url-bookmark-capture.automation.json +132 -0
- package/capabilities/agency-skills.capability.json +157 -0
- package/capabilities/apify.capability.json +265 -0
- package/capabilities/apollo.capability.json +120 -0
- package/capabilities/arch-backend.capability.json +97 -0
- package/capabilities/arch-client-intelligence.capability.json +401 -0
- package/capabilities/backblaze-b2.capability.json +205 -0
- package/capabilities/cal-com.capability.json +266 -0
- package/capabilities/canva.capability.json +274 -0
- package/capabilities/claude-code-routines.capability.json +172 -0
- package/capabilities/claude-managed-agents.capability.json +215 -0
- package/capabilities/client-comms-report.capability.json +246 -0
- package/capabilities/content-idea-to-production.capability.json +29 -0
- package/capabilities/datagouv.capability.json +315 -0
- package/capabilities/discord-bot.capability.json +157 -0
- package/capabilities/dropbox.capability.json +281 -0
- package/capabilities/exa.capability.json +109 -0
- package/capabilities/fal-ai.capability.json +83 -0
- package/capabilities/fal-media-lab.capability.json +158 -0
- package/capabilities/fireflies.capability.json +191 -0
- package/capabilities/freellmapi.capability.json +114 -0
- package/capabilities/generic-apikey.capability.json +92 -0
- package/capabilities/github-merge.capability.json +99 -0
- package/capabilities/github-read.capability.json +325 -0
- package/capabilities/governance-tighten-recommender.capability.json +81 -0
- package/capabilities/jev.capability.json +226 -0
- package/capabilities/lead-conversion-enrollment.capability.json +246 -0
- package/capabilities/mail-feed.capability.json +277 -0
- package/capabilities/mailgun.capability.json +52 -0
- package/capabilities/nango-google.capability.json +639 -0
- package/capabilities/notion.capability.json +417 -0
- package/capabilities/omniroute.capability.json +137 -0
- package/capabilities/posthog-analytics.capability.json +258 -0
- package/capabilities/postiz.capability.json +467 -0
- package/capabilities/proactive-digest.capability.json +190 -0
- package/capabilities/proton.capability.json +288 -0
- package/capabilities/replicate-rvm.capability.json +97 -0
- package/capabilities/research-methods.capability.json +57 -0
- package/capabilities/shopify.capability.json +395 -0
- package/capabilities/slack.capability.json +369 -0
- package/capabilities/stripe.capability.json +1496 -0
- package/capabilities/telegram-bridge.capability.json +56 -0
- package/capabilities/treg.capability.json +102 -0
- package/capabilities/unipile-linkedin.capability.json +494 -0
- package/capabilities/vercel.capability.json +424 -0
- package/dist/define-DqoItRiR.d.ts +1370 -0
- package/dist/index.d.ts +174 -900
- package/dist/index.js +26447 -24075
- package/dist/index.js.map +1 -1
- package/dist/wire.d.ts +222 -0
- package/dist/wire.js +77 -0
- package/dist/wire.js.map +1 -0
- package/package.json +27 -4
- package/retired.yaml +136 -0
- package/skills/app-factory-build-slice/SKILL.md +70 -0
- package/skills/app-factory-build-slice.catalog.json +17 -0
- package/skills/app-factory-design-canvas/SKILL.md +96 -0
- package/skills/app-factory-design-canvas.catalog.json +17 -0
- package/skills/app-factory-design-review/SKILL.md +105 -0
- package/skills/app-factory-design-review.catalog.json +18 -0
- package/skills/app-factory-research/SKILL.md +123 -0
- package/skills/app-factory-research.catalog.json +17 -0
- package/skills/bookmark-enrichment/SKILL.md +40 -0
- package/skills/carousel-format/carousel-format.skill.json +18 -0
- package/skills/collage-broll/collage-broll.skill.json +20 -0
- package/skills/content-taste/content-taste.skill.json +18 -0
- package/skills/creative-director/creative-director.skill.json +18 -0
- package/skills/design-critique/SKILL.md +138 -0
- package/skills/design-critique.catalog.json +12 -0
- package/skills/designing-with-real-components/SKILL.md +140 -0
- package/skills/designing-with-real-components.catalog.json +12 -0
- package/skills/edit-loop/edit-loop.skill.json +25 -0
- package/skills/facecam-edit/facecam-edit.skill.json +24 -0
- package/skills/mapping-the-server/SKILL.md +54 -0
- package/skills/math-explainer-manimgx/math-explainer-manimgx.skill.json +20 -0
- package/skills/motion-quality-gates/motion-quality-gates.skill.json +20 -0
- package/skills/open-design-edit/open-design-edit.skill.json +24 -0
- package/skills/reading-client-progress/SKILL.md +65 -0
- package/skills/routing-to-channels/SKILL.md +54 -0
- package/skills/stellar-scf-grants-advisor/ASSOCIATE-CHEATSHEET.md +133 -0
- package/skills/stellar-scf-grants-advisor/ASSOCIATE-ONBOARDING-GUIDE.md +761 -0
- package/skills/stellar-scf-grants-advisor/CLIENT-FAQ.md +181 -0
- package/skills/stellar-scf-grants-advisor/PARTNER-HANDOVER.md +130 -0
- package/skills/stellar-scf-grants-advisor/SKILL.md +102 -0
- package/skills/stellar-scf-grants-advisor/examples/README.md +43 -0
- package/skills/stellar-scf-grants-advisor/examples/chat-summary-templates.md +158 -0
- package/skills/stellar-scf-grants-advisor/examples/corpus-comparable-rows.md +136 -0
- package/skills/stellar-scf-grants-advisor/examples/q10-team-rewrites.md +109 -0
- package/skills/stellar-scf-grants-advisor/examples/q2-description-rewrites.md +88 -0
- package/skills/stellar-scf-grants-advisor/examples/q6-integration-rewrites.md +107 -0
- package/skills/stellar-scf-grants-advisor/examples/tranche-structures.md +176 -0
- package/skills/stellar-scf-grants-advisor/reference/01-methodology.md +96 -0
- package/skills/stellar-scf-grants-advisor/reference/02-scoring-framework.md +199 -0
- package/skills/stellar-scf-grants-advisor/reference/03-rejection-patterns.md +241 -0
- package/skills/stellar-scf-grants-advisor/reference/04-winner-patterns.md +120 -0
- package/skills/stellar-scf-grants-advisor/reference/05-comeback-patterns.md +92 -0
- package/skills/stellar-scf-grants-advisor/reference/06-budget-benchmarks.md +125 -0
- package/skills/stellar-scf-grants-advisor/reference/07-integration-list.md +147 -0
- package/skills/stellar-scf-grants-advisor/reference/08-abstract-fields.md +258 -0
- package/skills/stellar-scf-grants-advisor/reference/09-build-fields.md +259 -0
- package/skills/stellar-scf-grants-advisor/reference/10-corpus-winners.md +135 -0
- package/skills/stellar-scf-grants-advisor/reference/11-corpus-rejects.md +267 -0
- package/skills/stellar-scf-grants-advisor/reference/12-client-communication.md +131 -0
- package/skills/stellar-scf-grants-advisor/reference/13-excel-output-spec.md +199 -0
- package/skills/stellar-scf-grants-advisor/reference/14-worked-examples.md +160 -0
- package/skills/stellar-scf-grants-advisor/templates/abstract-review-builder.py +253 -0
- package/skills/stellar-scf-grants-advisor/templates/build-review-builder.py +213 -0
- package/skills/video-direction/video-direction.skill.json +20 -0
- package/skills/video-style-vocabulary/video-style-vocabulary.skill.json +20 -0
- package/skills/video-toolbox/video-toolbox.skill.json +20 -0
- package/src/__fixtures__/lifecycle-presets.flat-before.json +205 -0
- package/src/agency-os.yaml +122 -0
- package/src/agent-os-skill-slugs.test.ts +65 -0
- package/src/apify-social-reader.test.ts +317 -0
- package/src/base.yaml +85 -39
- package/src/blockchain-ecosystem.yaml +1 -0
- package/src/brand-library.yaml +525 -14
- package/src/builder-dev-fold.template.test.ts +113 -0
- package/src/builder.yaml +779 -91
- package/src/business-model-catalog-description.test.ts +47 -0
- package/src/business-model-grp.template.test.ts +214 -0
- package/src/business-model.yaml +296 -49
- package/src/catalog-safety.test.ts +198 -0
- package/src/catalog.template-items.test.ts +75 -0
- package/src/catalog.ts +66 -2
- package/src/concept-guard.test.ts +28 -8
- package/src/concept-guard.ts +2 -2
- package/src/content-creation.template.test.ts +52 -0
- package/src/content-creation.yaml +192 -0
- package/src/content-os.edit-loop.template.test.ts +210 -0
- package/src/content-os.publish-due-posts.template.test.ts +261 -0
- package/src/content-os.template.test.ts +349 -0
- package/src/content-os.video-takes.template.test.ts +338 -0
- package/src/content-os.work-a-take.template.test.ts +125 -0
- package/src/content-os.yaml +1873 -39
- package/src/content-studio-retired.template.test.ts +82 -0
- package/src/corpus-flow-validity.test.ts +4 -5
- package/src/crm-absorbs-outreach.template.test.ts +117 -0
- package/src/crm-build-plan.template.test.ts +128 -0
- package/src/crm-lists.template.test.ts +96 -0
- package/src/crm-messages-cadence.template.test.ts +246 -0
- package/src/crm-one-send-door.template.test.ts +65 -0
- package/src/crm.yaml +1563 -181
- package/src/decisions-wave.template.test.ts +186 -0
- package/src/define.ts +146 -17
- package/src/design-to-implementation.template.test.ts +94 -0
- package/src/domain-template-type.test.ts +75 -0
- package/src/ecosystem.yaml +27 -66
- package/src/enterprise-os.yaml +10 -134
- package/src/facecam-edit.audio-studio.test.ts +188 -0
- package/src/fal-generate-image.test.ts +200 -0
- package/src/fal-media-lab-pod-files.test.ts +180 -0
- package/src/finance.yaml +254 -1
- package/src/foundation.yaml +78 -38
- package/src/frame-host-modules.d.mts +6 -0
- package/src/frame-host-modules.mjs +58 -0
- package/src/generate.mjs +9 -3
- package/src/github-merge.test.ts +210 -0
- package/src/grants.yaml +46 -17
- package/src/hr.yaml +39 -6
- package/src/index.ts +36 -1
- package/src/intent.test.ts +161 -0
- package/src/internal-runbook.yaml +19 -1
- package/src/jev.test.ts +356 -0
- package/src/legal.yaml +33 -1
- package/src/life-os.yaml +51 -240
- package/src/lifecycle-coverage.test.ts +124 -0
- package/src/lifecycle-declarations.test.ts +252 -0
- package/src/lifecycle-presets.test.ts +468 -0
- package/src/lifecycle-presets.ts +494 -0
- package/src/lifecycle-stuck.test.ts +46 -0
- package/src/lifecycle-waits.parity.test.ts +319 -0
- package/src/marketing.yaml +108 -4
- package/src/meetings-knowledge.yaml +18 -0
- package/src/networking.yaml +23 -2
- package/src/operations.yaml +304 -4
- package/src/pipeline-views.template.test.ts +88 -0
- package/src/playbook-goal-placeholders.test.ts +57 -0
- package/src/postiz-contract.test.ts +134 -0
- package/src/presentation-declarations.test.ts +174 -0
- package/src/process-contract.validate.test.ts +352 -0
- package/src/project-management.yaml +39 -740
- package/src/publish-body.d.mts +29 -0
- package/src/publish-body.mjs +74 -0
- package/src/publish-body.test.ts +74 -0
- package/src/publish-official-lib.test.ts +176 -0
- package/src/replicate-rvm.test.ts +504 -0
- package/src/research-base.yaml +119 -20
- package/src/research-decision.yaml +60 -3
- package/src/reserved-profile-slugs.test.ts +81 -0
- package/src/retired-goal-kinds.test.ts +52 -0
- package/src/role-reference-and-step-domain.test.ts +192 -0
- package/src/seed-obligation.test.ts +1 -6
- package/src/send-message-maps.test.ts +227 -0
- package/src/shared-role-ssot.test.ts +40 -2
- package/src/ship-a-change.template.test.ts +177 -0
- package/src/skills.test.ts +170 -0
- package/src/social.yaml +85 -27
- package/src/space-packages.freshness.test.ts +97 -0
- package/src/space-packages.generated.ts +40 -0
- package/src/space-packages.test.ts +125 -0
- package/src/space-packages.ts +73 -0
- package/src/start.test.ts +188 -0
- package/src/stripe-capability.test.ts +192 -0
- package/src/task-intents.test.ts +293 -0
- package/src/template-documents.test.ts +158 -0
- package/src/templates.ts +20907 -21464
- package/src/types.ts +475 -61
- package/src/unipile-linkedin-find-profile.test.ts +183 -0
- package/src/unipile-publish-post.test.ts +162 -0
- package/src/validate-flow.drift.test.ts +2 -3
- package/src/validate-flow.ts +22 -2
- package/src/validate-onboarding.test.ts +59 -0
- package/src/validate.test.ts +71 -4
- package/src/validate.ts +1416 -7
- package/src/wire-core.ts +231 -0
- package/src/wire.test.ts +159 -0
- package/src/wire.ts +107 -0
- package/views/bookmark-views-pack.view.json +39 -0
- package/views/event-views-pack.view.json +45 -0
- package/views/task-views-pack.view.json +53 -0
- package/src/agent-fleet.yaml +0 -1645
- package/src/business-developer.template.test.ts +0 -351
- package/src/business-developer.yaml +0 -860
- package/src/content-studio.yaml +0 -361
- package/src/dev-dashboard.yaml +0 -990
- package/src/outreach-comms.yaml +0 -486
- package/src/the-arch.yaml +0 -201
|
@@ -0,0 +1,504 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Replicate Robust Video Matting (capability `replicate-rvm`): the marketplace
|
|
3
|
+
* tool that serves `edit_video` (founder decisions be0deab7 and ac1ba2b6,
|
|
4
|
+
* 2026-10-28). The app's input is `{ video: <https URL> }`.
|
|
5
|
+
*
|
|
6
|
+
* SEAM: each verb's REAL `code` body, run the way the pod's executor wraps it
|
|
7
|
+
* (`async (args, context) => { <code> }` with `callProvider` in scope),
|
|
8
|
+
* against Replicate responses shaped like its documented prediction object
|
|
9
|
+
* (`{ id, version, input, status, output, error }`). The request asserted is
|
|
10
|
+
* exactly what `callProvider` would send to api.replicate.com/v1.
|
|
11
|
+
*
|
|
12
|
+
* The long-run design under test: an isolate has no timer, so the matte verb
|
|
13
|
+
* starts every prediction, waits on the LAST one only (`Prefer: wait=60`),
|
|
14
|
+
* reads the others once, and hands anything unfinished to the read verb
|
|
15
|
+
* through `next` instead of starting the predictions again.
|
|
16
|
+
*
|
|
17
|
+
* NOT covered: Replicate's live behaviour (payloads are hand-written from its
|
|
18
|
+
* API page), the pod's propose-moment arg check and the IS isolate (Node's
|
|
19
|
+
* AsyncFunction is used).
|
|
20
|
+
*/
|
|
21
|
+
import { readFileSync } from "node:fs";
|
|
22
|
+
import { dirname, join } from "node:path";
|
|
23
|
+
import { fileURLToPath } from "node:url";
|
|
24
|
+
import { describe, expect, it } from "vitest";
|
|
25
|
+
|
|
26
|
+
const HERE = dirname(fileURLToPath(import.meta.url));
|
|
27
|
+
const manifest = JSON.parse(
|
|
28
|
+
readFileSync(
|
|
29
|
+
join(HERE, "../capabilities/replicate-rvm.capability.json"),
|
|
30
|
+
"utf8"
|
|
31
|
+
)
|
|
32
|
+
);
|
|
33
|
+
type Skill = {
|
|
34
|
+
name: string;
|
|
35
|
+
intent?: string;
|
|
36
|
+
code?: string;
|
|
37
|
+
parameters?: Record<string, string>;
|
|
38
|
+
metadata?: Record<string, unknown>;
|
|
39
|
+
};
|
|
40
|
+
const skills = manifest.spec.skills as Skill[];
|
|
41
|
+
const verb = (name: string) => skills.find((s) => s.name === name)!;
|
|
42
|
+
|
|
43
|
+
const AsyncFunction = Object.getPrototypeOf(async () => {}).constructor as new (
|
|
44
|
+
...args: string[]
|
|
45
|
+
) => (...a: unknown[]) => Promise<unknown>;
|
|
46
|
+
const matte = new AsyncFunction(
|
|
47
|
+
"args",
|
|
48
|
+
"context",
|
|
49
|
+
"callProvider",
|
|
50
|
+
verb("replicate_rvm_matte").code!
|
|
51
|
+
);
|
|
52
|
+
const result = new AsyncFunction(
|
|
53
|
+
"args",
|
|
54
|
+
"context",
|
|
55
|
+
"callProvider",
|
|
56
|
+
verb("replicate_rvm_result").code!
|
|
57
|
+
);
|
|
58
|
+
|
|
59
|
+
const VERSION =
|
|
60
|
+
"73d2128a371922d5d1abf0712a1d974be0e4e2358cc1218e4e34714767232bac";
|
|
61
|
+
const VIDEO =
|
|
62
|
+
"https://pod.example.synap.live/files/take-1.mp4?sig=abc&exp=1730000000";
|
|
63
|
+
const OUT = (t: string) => `https://replicate.delivery/xezq/${t}/output.mp4`;
|
|
64
|
+
|
|
65
|
+
type Call = {
|
|
66
|
+
tool: string;
|
|
67
|
+
method: string;
|
|
68
|
+
path: string;
|
|
69
|
+
body?: Record<string, any>;
|
|
70
|
+
opts?: Record<string, any>;
|
|
71
|
+
};
|
|
72
|
+
type Pred = {
|
|
73
|
+
id: string;
|
|
74
|
+
status: string;
|
|
75
|
+
output?: unknown;
|
|
76
|
+
error?: string | null;
|
|
77
|
+
type: string;
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
/** A fake Replicate: POST creates a prediction in the state `onCreate` says; GET returns `onGet`. */
|
|
81
|
+
function replicate(opts: {
|
|
82
|
+
onCreate: (type: string, waited: boolean) => Omit<Pred, "id" | "type">;
|
|
83
|
+
onGet?: (p: Pred) => Omit<Pred, "id" | "type">;
|
|
84
|
+
}) {
|
|
85
|
+
const calls: Call[] = [];
|
|
86
|
+
const preds = new Map<string, Pred>();
|
|
87
|
+
let n = 0;
|
|
88
|
+
const callProvider = async (
|
|
89
|
+
tool: string,
|
|
90
|
+
method: string,
|
|
91
|
+
path: string,
|
|
92
|
+
body?: Record<string, any>,
|
|
93
|
+
o?: Record<string, any>
|
|
94
|
+
) => {
|
|
95
|
+
calls.push({ tool, method, path, body, opts: o });
|
|
96
|
+
if (method === "POST") {
|
|
97
|
+
const type = body!.input.output_type;
|
|
98
|
+
const p: Pred = {
|
|
99
|
+
id: `pred${++n}abcdefgh`,
|
|
100
|
+
type,
|
|
101
|
+
...opts.onCreate(type, o?.headers?.Prefer === "wait=60"),
|
|
102
|
+
};
|
|
103
|
+
preds.set(p.id, p);
|
|
104
|
+
return {
|
|
105
|
+
status: 201,
|
|
106
|
+
headers: {},
|
|
107
|
+
body: {
|
|
108
|
+
id: p.id,
|
|
109
|
+
version: body!.version,
|
|
110
|
+
input: body!.input,
|
|
111
|
+
status: p.status,
|
|
112
|
+
output: p.output ?? null,
|
|
113
|
+
error: p.error ?? null,
|
|
114
|
+
},
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
const id = path.split("/").pop()!;
|
|
118
|
+
const p = preds.get(id) ?? {
|
|
119
|
+
id,
|
|
120
|
+
type: "alpha-mask",
|
|
121
|
+
status: "succeeded",
|
|
122
|
+
output: OUT(id),
|
|
123
|
+
};
|
|
124
|
+
const next = opts.onGet ? { ...p, ...opts.onGet(p) } : p;
|
|
125
|
+
preds.set(id, next);
|
|
126
|
+
return {
|
|
127
|
+
status: 200,
|
|
128
|
+
headers: {},
|
|
129
|
+
body: {
|
|
130
|
+
id,
|
|
131
|
+
version: VERSION,
|
|
132
|
+
input: { input_video: VIDEO, output_type: next.type },
|
|
133
|
+
status: next.status,
|
|
134
|
+
output: next.output ?? null,
|
|
135
|
+
error: next.error ?? null,
|
|
136
|
+
},
|
|
137
|
+
};
|
|
138
|
+
};
|
|
139
|
+
return { calls, preds, callProvider };
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
describe("replicate-rvm — the capability serves edit_video", () => {
|
|
143
|
+
it("declares edit_video on the matte verb, a read-only result verb, and provides exactly what the verbs declare", () => {
|
|
144
|
+
expect(verb("replicate_rvm_matte")).toMatchObject({
|
|
145
|
+
intent: "edit_video",
|
|
146
|
+
parameters: {
|
|
147
|
+
video: "string?",
|
|
148
|
+
videoFile: "file:url?",
|
|
149
|
+
outputs: "array?",
|
|
150
|
+
},
|
|
151
|
+
});
|
|
152
|
+
expect(verb("replicate_rvm_result")).toMatchObject({
|
|
153
|
+
intent: "fetch_record",
|
|
154
|
+
metadata: { readOnly: true },
|
|
155
|
+
parameters: { predictionIds: "array" },
|
|
156
|
+
});
|
|
157
|
+
expect([...manifest.spec.provides].sort()).toEqual([
|
|
158
|
+
"edit_video",
|
|
159
|
+
"fetch_record",
|
|
160
|
+
]);
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
it("maps the edit_video input { video } onto the matte verb, so a pod app's edit_video action reaches it (0368 inputMap)", () => {
|
|
164
|
+
expect(
|
|
165
|
+
(verb("replicate_rvm_matte") as Skill & { inputMap?: unknown }).inputMap
|
|
166
|
+
).toEqual({ params: { video: "$.video" } });
|
|
167
|
+
expect(
|
|
168
|
+
(verb("replicate_rvm_result") as Skill & { inputMap?: unknown }).inputMap
|
|
169
|
+
).toBeUndefined();
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
it("keeps the token in the vault, sent as a Bearer header to api.replicate.com/v1 only", () => {
|
|
173
|
+
const tool = manifest.spec.tools[0];
|
|
174
|
+
expect(tool).toMatchObject({
|
|
175
|
+
name: "replicate_api",
|
|
176
|
+
kind: "api",
|
|
177
|
+
credentialRef: "replicateApiTokenSecret",
|
|
178
|
+
});
|
|
179
|
+
expect(tool.config).toEqual({
|
|
180
|
+
baseUrl: "https://api.replicate.com/v1",
|
|
181
|
+
auth: { in: "header", name: "Authorization", prefix: "Bearer " },
|
|
182
|
+
});
|
|
183
|
+
expect(manifest.spec.vault[0]).toMatchObject({
|
|
184
|
+
ref: "replicateApiTokenSecret",
|
|
185
|
+
value: "{{replicateApiToken}}",
|
|
186
|
+
});
|
|
187
|
+
expect(manifest.spec.params).toEqual([
|
|
188
|
+
expect.objectContaining({ name: "replicateApiToken", required: true }),
|
|
189
|
+
]);
|
|
190
|
+
});
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
describe("replicate_rvm_matte — one call on a warm model", () => {
|
|
194
|
+
it("starts alpha then foreground on the pinned version, waits only on the last, and returns both URLs", async () => {
|
|
195
|
+
const r = replicate({
|
|
196
|
+
onCreate: (type, waited) =>
|
|
197
|
+
waited
|
|
198
|
+
? { status: "succeeded", output: OUT(type) }
|
|
199
|
+
: { status: "starting" },
|
|
200
|
+
onGet: (p) => ({ status: "succeeded", output: OUT(p.type) }),
|
|
201
|
+
});
|
|
202
|
+
const raw = (await matte({ video: VIDEO }, {}, r.callProvider)) as Record<
|
|
203
|
+
string,
|
|
204
|
+
any
|
|
205
|
+
>;
|
|
206
|
+
const posts = r.calls.filter((c) => c.method === "POST");
|
|
207
|
+
expect(posts.map((c) => c.body)).toEqual([
|
|
208
|
+
{
|
|
209
|
+
version: VERSION,
|
|
210
|
+
input: { input_video: VIDEO, output_type: "alpha-mask" },
|
|
211
|
+
},
|
|
212
|
+
{
|
|
213
|
+
version: VERSION,
|
|
214
|
+
input: { input_video: VIDEO, output_type: "foreground-mask" },
|
|
215
|
+
},
|
|
216
|
+
]);
|
|
217
|
+
expect(posts.map((c) => c.opts?.headers?.Prefer ?? null)).toEqual([
|
|
218
|
+
null,
|
|
219
|
+
"wait=60",
|
|
220
|
+
]);
|
|
221
|
+
expect(
|
|
222
|
+
posts.every(
|
|
223
|
+
(c) => c.tool === "replicate_api" && c.path === "/predictions"
|
|
224
|
+
)
|
|
225
|
+
).toBe(true);
|
|
226
|
+
// The first one ran while the second waited: read once.
|
|
227
|
+
expect(
|
|
228
|
+
r.calls.filter((c) => c.method === "GET").map((c) => c.path)
|
|
229
|
+
).toEqual(["/predictions/pred1abcdefgh"]);
|
|
230
|
+
expect(raw).toMatchObject({
|
|
231
|
+
success: true,
|
|
232
|
+
status: "succeeded",
|
|
233
|
+
alphaUrl: OUT("alpha-mask"),
|
|
234
|
+
foregroundUrl: OUT("foreground-mask"),
|
|
235
|
+
next: null,
|
|
236
|
+
});
|
|
237
|
+
expect(raw.predictionIds).toEqual(["pred1abcdefgh", "pred2abcdefgh"]);
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
it("a single output waits on that one and reads nothing", async () => {
|
|
241
|
+
const r = replicate({
|
|
242
|
+
onCreate: () => ({ status: "succeeded", output: OUT("a") }),
|
|
243
|
+
});
|
|
244
|
+
const raw = (await matte(
|
|
245
|
+
{ video: VIDEO, outputs: ["alpha", "alpha"] },
|
|
246
|
+
{},
|
|
247
|
+
r.callProvider
|
|
248
|
+
)) as Record<string, any>;
|
|
249
|
+
expect(
|
|
250
|
+
r.calls.map((c) => `${c.method} ${c.opts?.headers?.Prefer ?? ""}`)
|
|
251
|
+
).toEqual(["POST wait=60"]);
|
|
252
|
+
expect(raw.alphaUrl).toBe(OUT("a"));
|
|
253
|
+
expect(raw.foregroundUrl).toBeUndefined();
|
|
254
|
+
});
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
describe("replicate_rvm_matte — a long take is handed to the read verb, never restarted", () => {
|
|
258
|
+
it("returns processing with next = replicate_rvm_result and the same ids", async () => {
|
|
259
|
+
const r = replicate({
|
|
260
|
+
onCreate: () => ({ status: "processing" }),
|
|
261
|
+
onGet: () => ({ status: "processing" }),
|
|
262
|
+
});
|
|
263
|
+
const raw = (await matte({ video: VIDEO }, {}, r.callProvider)) as Record<
|
|
264
|
+
string,
|
|
265
|
+
any
|
|
266
|
+
>;
|
|
267
|
+
expect(raw.status).toBe("processing");
|
|
268
|
+
expect(raw.alphaUrl).toBeUndefined();
|
|
269
|
+
expect(raw.next).toEqual({
|
|
270
|
+
verbId: "replicate_rvm_result",
|
|
271
|
+
parameters: { predictionIds: ["pred1abcdefgh", "pred2abcdefgh"] },
|
|
272
|
+
});
|
|
273
|
+
expect(r.calls.filter((c) => c.method === "POST")).toHaveLength(2);
|
|
274
|
+
});
|
|
275
|
+
|
|
276
|
+
it("the read verb finishes it: GET only, both URLs by output type", async () => {
|
|
277
|
+
let finished = false;
|
|
278
|
+
const r = replicate({
|
|
279
|
+
onCreate: () => ({ status: "processing" }),
|
|
280
|
+
onGet: (p) =>
|
|
281
|
+
finished
|
|
282
|
+
? { status: "succeeded", output: OUT(p.type) }
|
|
283
|
+
: { status: "processing" },
|
|
284
|
+
});
|
|
285
|
+
const first = (await matte({ video: VIDEO }, {}, r.callProvider)) as Record<
|
|
286
|
+
string,
|
|
287
|
+
any
|
|
288
|
+
>;
|
|
289
|
+
finished = true;
|
|
290
|
+
const calls = r.calls.length;
|
|
291
|
+
const done = (await result(
|
|
292
|
+
first.next.parameters,
|
|
293
|
+
{},
|
|
294
|
+
async (...a: any[]) => {
|
|
295
|
+
// Only reads after this point.
|
|
296
|
+
expect(a[1]).toBe("GET");
|
|
297
|
+
return (r.callProvider as any)(...a);
|
|
298
|
+
}
|
|
299
|
+
)) as Record<string, any>;
|
|
300
|
+
expect(r.calls.length - calls).toBe(2);
|
|
301
|
+
expect(done).toMatchObject({
|
|
302
|
+
success: true,
|
|
303
|
+
status: "succeeded",
|
|
304
|
+
alphaUrl: OUT("alpha-mask"),
|
|
305
|
+
foregroundUrl: OUT("foreground-mask"),
|
|
306
|
+
next: null,
|
|
307
|
+
});
|
|
308
|
+
});
|
|
309
|
+
|
|
310
|
+
it("the read verb refuses an id that is not an RVM prediction", async () => {
|
|
311
|
+
const callProvider = async () => ({
|
|
312
|
+
status: 200,
|
|
313
|
+
body: {
|
|
314
|
+
id: "otherpred123",
|
|
315
|
+
version: "0".repeat(64),
|
|
316
|
+
input: {},
|
|
317
|
+
status: "succeeded",
|
|
318
|
+
output: OUT("x"),
|
|
319
|
+
},
|
|
320
|
+
});
|
|
321
|
+
await expect(
|
|
322
|
+
result({ predictionIds: ["otherpred123"] }, {}, callProvider)
|
|
323
|
+
).rejects.toThrow(/not a Robust Video Matting prediction/);
|
|
324
|
+
});
|
|
325
|
+
});
|
|
326
|
+
|
|
327
|
+
describe("replicate_rvm_matte — failures are loud", () => {
|
|
328
|
+
it("a failed prediction fails the run with Replicate's error", async () => {
|
|
329
|
+
const r = replicate({
|
|
330
|
+
onCreate: (_t, waited) =>
|
|
331
|
+
waited
|
|
332
|
+
? { status: "failed", error: "CUDA out of memory" }
|
|
333
|
+
: { status: "starting" },
|
|
334
|
+
onGet: () => ({ status: "processing" }),
|
|
335
|
+
});
|
|
336
|
+
await expect(matte({ video: VIDEO }, {}, r.callProvider)).rejects.toThrow(
|
|
337
|
+
/foreground prediction pred2abcdefgh failed: CUDA out of memory/
|
|
338
|
+
);
|
|
339
|
+
});
|
|
340
|
+
|
|
341
|
+
it("a refused create names the status and Replicate's detail", async () => {
|
|
342
|
+
const callProvider = async () => ({
|
|
343
|
+
status: 402,
|
|
344
|
+
body: { detail: "You have insufficient credit" },
|
|
345
|
+
});
|
|
346
|
+
await expect(matte({ video: VIDEO }, {}, callProvider)).rejects.toThrow(
|
|
347
|
+
/status 402\): You have insufficient credit/
|
|
348
|
+
);
|
|
349
|
+
});
|
|
350
|
+
|
|
351
|
+
it("a proposal envelope is returned as the record, nothing else runs", async () => {
|
|
352
|
+
const calls: string[] = [];
|
|
353
|
+
const callProvider = async (_t: string, m: string) => (
|
|
354
|
+
calls.push(m),
|
|
355
|
+
{ proposed: true, proposalId: "p1" }
|
|
356
|
+
);
|
|
357
|
+
expect(await matte({ video: VIDEO }, {}, callProvider)).toEqual({
|
|
358
|
+
success: false,
|
|
359
|
+
proposed: true,
|
|
360
|
+
proposalId: "p1",
|
|
361
|
+
started: [],
|
|
362
|
+
});
|
|
363
|
+
expect(calls).toEqual(["POST"]);
|
|
364
|
+
});
|
|
365
|
+
});
|
|
366
|
+
|
|
367
|
+
describe("replicate_rvm_matte — strict inputs, nothing undeclared reaches Replicate", () => {
|
|
368
|
+
const ok = () =>
|
|
369
|
+
replicate({ onCreate: () => ({ status: "succeeded", output: OUT("a") }) });
|
|
370
|
+
|
|
371
|
+
it.each([
|
|
372
|
+
["no video", {}],
|
|
373
|
+
["http, not https", { video: "http://example.com/a.mp4" }],
|
|
374
|
+
[
|
|
375
|
+
"credentials in the URL",
|
|
376
|
+
{ video: "https://user:pass@example.com/a.mp4" },
|
|
377
|
+
],
|
|
378
|
+
["a data URL", { video: "data:video/mp4;base64,AAAA" }],
|
|
379
|
+
["a vault ref", { video: "vault://abc" }],
|
|
380
|
+
[
|
|
381
|
+
"a URL over the intent's 2048 characters",
|
|
382
|
+
{ video: "https://example.com/" + "a".repeat(2029) },
|
|
383
|
+
],
|
|
384
|
+
["an unknown output", { video: VIDEO, outputs: ["depth"] }],
|
|
385
|
+
["an empty outputs list", { video: VIDEO, outputs: [] }],
|
|
386
|
+
["Replicate's own spelling", { input_video: VIDEO }],
|
|
387
|
+
])("refuses %s before any call", async (_why, args) => {
|
|
388
|
+
const r = ok();
|
|
389
|
+
await expect(matte(args, {}, r.callProvider)).rejects.toThrow(
|
|
390
|
+
/replicate_rvm_matte:/
|
|
391
|
+
);
|
|
392
|
+
expect(r.calls).toEqual([]);
|
|
393
|
+
});
|
|
394
|
+
|
|
395
|
+
it("undeclared keys are never forwarded", async () => {
|
|
396
|
+
const r = ok();
|
|
397
|
+
await matte(
|
|
398
|
+
{
|
|
399
|
+
video: VIDEO,
|
|
400
|
+
version: "evil",
|
|
401
|
+
input: { x: 1 },
|
|
402
|
+
webhook: "https://evil.example",
|
|
403
|
+
apiKey: "sk",
|
|
404
|
+
},
|
|
405
|
+
{},
|
|
406
|
+
r.callProvider
|
|
407
|
+
);
|
|
408
|
+
for (const c of r.calls.filter((x) => x.method === "POST")) {
|
|
409
|
+
expect(Object.keys(c.body!).sort()).toEqual(["input", "version"]);
|
|
410
|
+
expect(c.body!.version).toBe(VERSION);
|
|
411
|
+
expect(Object.keys(c.body!.input).sort()).toEqual([
|
|
412
|
+
"input_video",
|
|
413
|
+
"output_type",
|
|
414
|
+
]);
|
|
415
|
+
}
|
|
416
|
+
});
|
|
417
|
+
|
|
418
|
+
it("the read verb refuses a malformed id before any call", async () => {
|
|
419
|
+
const calls: unknown[] = [];
|
|
420
|
+
await expect(
|
|
421
|
+
result({ predictionIds: ["../account"] }, {}, async (...a: unknown[]) =>
|
|
422
|
+
calls.push(a)
|
|
423
|
+
)
|
|
424
|
+
).rejects.toThrow(/not a Replicate prediction id/);
|
|
425
|
+
expect(calls).toEqual([]);
|
|
426
|
+
});
|
|
427
|
+
});
|
|
428
|
+
|
|
429
|
+
/**
|
|
430
|
+
* Phase 1c (pod files to tools by URL, backend #469): the take is a POD FILE.
|
|
431
|
+
* The caller passes its file entity id as `videoFile` (declared `file:url`);
|
|
432
|
+
* the pod mints a fresh signed URL at run time and swaps it in, so the skill
|
|
433
|
+
* receives an https URL. The verb is async so that URL lives 6 h (a cold
|
|
434
|
+
* Replicate start may fetch it late). The edit_video intent input stays an
|
|
435
|
+
* https `video`, mapped by inputMap.
|
|
436
|
+
*/
|
|
437
|
+
describe("replicate_rvm_matte — a pod file in, signed by the pod", () => {
|
|
438
|
+
const SIGNED =
|
|
439
|
+
"https://s3.example.synap.live/pod/files/take-1.mp4?X-Amz-Signature=secret123&X-Amz-Expires=21600";
|
|
440
|
+
|
|
441
|
+
it("declares videoFile as a pod file by URL, async (6 h link), never read-only", () => {
|
|
442
|
+
const m = verb("replicate_rvm_matte") as Skill & { executionMode?: string };
|
|
443
|
+
expect(m.parameters?.videoFile).toBe("file:url?");
|
|
444
|
+
expect(m.executionMode).toBe("async");
|
|
445
|
+
expect(m.metadata?.readOnly).toBeUndefined();
|
|
446
|
+
expect((m as Skill & { inputMap?: unknown }).inputMap).toEqual({
|
|
447
|
+
params: { video: "$.video" },
|
|
448
|
+
});
|
|
449
|
+
});
|
|
450
|
+
|
|
451
|
+
it("sends the signed URL the pod swapped in as Replicate's input_video, and says the source", async () => {
|
|
452
|
+
const r = replicate({
|
|
453
|
+
onCreate: () => ({ status: "succeeded", output: OUT("a") }),
|
|
454
|
+
});
|
|
455
|
+
const raw = (await matte(
|
|
456
|
+
{ videoFile: SIGNED },
|
|
457
|
+
{},
|
|
458
|
+
r.callProvider
|
|
459
|
+
)) as Record<string, any>;
|
|
460
|
+
const posts = r.calls.filter((c) => c.method === "POST");
|
|
461
|
+
expect(posts.map((c) => c.body!.input.input_video)).toEqual([
|
|
462
|
+
SIGNED,
|
|
463
|
+
SIGNED,
|
|
464
|
+
]);
|
|
465
|
+
expect(raw.source).toBe("pod-file");
|
|
466
|
+
expect(JSON.stringify(raw)).not.toContain("X-Amz-Signature");
|
|
467
|
+
});
|
|
468
|
+
|
|
469
|
+
it("a URL caller is still served, marked as such", async () => {
|
|
470
|
+
const r = replicate({
|
|
471
|
+
onCreate: () => ({ status: "succeeded", output: OUT("a") }),
|
|
472
|
+
});
|
|
473
|
+
const raw = (await matte({ video: VIDEO }, {}, r.callProvider)) as Record<
|
|
474
|
+
string,
|
|
475
|
+
any
|
|
476
|
+
>;
|
|
477
|
+
expect(raw.source).toBe("url");
|
|
478
|
+
});
|
|
479
|
+
|
|
480
|
+
it("refuses both, neither, and an unswapped file id, without echoing a signed URL", async () => {
|
|
481
|
+
const r = replicate({
|
|
482
|
+
onCreate: () => ({ status: "succeeded", output: OUT("a") }),
|
|
483
|
+
});
|
|
484
|
+
await expect(
|
|
485
|
+
matte({ video: VIDEO, videoFile: SIGNED }, {}, r.callProvider)
|
|
486
|
+
).rejects.toThrow(/not both/);
|
|
487
|
+
await expect(matte({}, {}, r.callProvider)).rejects.toThrow(
|
|
488
|
+
/video \(an https URL\) or videoFile \(a pod file\) is required/
|
|
489
|
+
);
|
|
490
|
+
const err = await matte(
|
|
491
|
+
{ videoFile: "8d2f4c1e-0b7a-4c55-9a7e-3f1f0d1c2b3a" },
|
|
492
|
+
{},
|
|
493
|
+
r.callProvider
|
|
494
|
+
).catch((e: Error) => e.message);
|
|
495
|
+
expect(err).toMatch(/did not arrive as a signed pod file URL/);
|
|
496
|
+
const http = await matte(
|
|
497
|
+
{ videoFile: "http://s3.example/x.mp4?X-Amz-Signature=secret123" },
|
|
498
|
+
{},
|
|
499
|
+
r.callProvider
|
|
500
|
+
).catch((e: Error) => e.message);
|
|
501
|
+
expect(http).not.toContain("secret123");
|
|
502
|
+
expect(r.calls).toEqual([]);
|
|
503
|
+
});
|
|
504
|
+
});
|
package/src/research-base.yaml
CHANGED
|
@@ -26,6 +26,12 @@ meta:
|
|
|
26
26
|
color: "#8B5CF6"
|
|
27
27
|
tags: [research, knowledge, graph, sources, evidence, learning]
|
|
28
28
|
isPublic: true
|
|
29
|
+
# What this space PRODUCES (homes): the purpose word, then its kinds/roles.
|
|
30
|
+
intent:
|
|
31
|
+
label: Research
|
|
32
|
+
produces:
|
|
33
|
+
- profile: hypothesis
|
|
34
|
+
- profile: finding
|
|
29
35
|
|
|
30
36
|
workspace:
|
|
31
37
|
name: Research
|
|
@@ -178,6 +184,15 @@ profiles:
|
|
|
178
184
|
icon: git-branch
|
|
179
185
|
color: violet
|
|
180
186
|
description: A candidate theory or answer under test — argued for and against by sources.
|
|
187
|
+
# The ONE lifecycle (work model L1): every option's category.
|
|
188
|
+
lifecycle:
|
|
189
|
+
property: stance
|
|
190
|
+
options:
|
|
191
|
+
- { key: proposed, category: open }
|
|
192
|
+
- { key: supported, category: done }
|
|
193
|
+
- { key: contested, category: active }
|
|
194
|
+
- { key: refuted, category: lost }
|
|
195
|
+
- { key: superseded, category: lost }
|
|
181
196
|
properties:
|
|
182
197
|
- slug: statement
|
|
183
198
|
label: Statement
|
|
@@ -204,6 +219,13 @@ profiles:
|
|
|
204
219
|
icon: check-circle
|
|
205
220
|
color: "#10B981"
|
|
206
221
|
description: A validated fact — the thing established between the question, the sources, and the reasoning. Carries calibrated confidence and links to its evidence.
|
|
222
|
+
# The ONE lifecycle (work model L1): every option's category.
|
|
223
|
+
lifecycle:
|
|
224
|
+
property: status
|
|
225
|
+
options:
|
|
226
|
+
- { key: validated, category: done }
|
|
227
|
+
- { key: provisional, category: open }
|
|
228
|
+
- { key: retracted, category: lost }
|
|
207
229
|
properties:
|
|
208
230
|
- slug: claim
|
|
209
231
|
label: Claim
|
|
@@ -369,33 +391,39 @@ entityLinks:
|
|
|
369
391
|
label: About,
|
|
370
392
|
}
|
|
371
393
|
|
|
372
|
-
#
|
|
373
|
-
#
|
|
374
|
-
# the full method: gather + rank
|
|
375
|
-
#
|
|
394
|
+
# "Research a Question" runs a question's lifecycle (open → exploring →
|
|
395
|
+
# answered): a new question proposes it, and any question can launch it from its
|
|
396
|
+
# detail page (see actionPlacements). It runs the full method: gather + rank
|
|
397
|
+
# sources (keep the rejects), form and weigh hypotheses, emit findings with
|
|
398
|
+
# honest confidence, and place them on the map. "Turn a finding into content"
|
|
399
|
+
# reads a finding's status: a validated finding proposes post ideas from it.
|
|
376
400
|
playbooks:
|
|
377
401
|
- name: Research a Question
|
|
378
402
|
description: Investigate a question end-to-end — gather and rank sources (keeping the rejected ones), weigh hypotheses, emit findings with calibrated confidence, and map them onto the knowledge graph.
|
|
379
403
|
goalTemplate: >-
|
|
380
|
-
Investigate the question {question} and record the WHOLE investigation,
|
|
381
|
-
just the answer
|
|
382
|
-
|
|
404
|
+
Investigate the question {question} and record the WHOLE investigation,
|
|
405
|
+
not just the answer; each stage moves the question's status. FRAME:
|
|
406
|
+
restate the question so it can be answered, and say what evidence would
|
|
407
|
+
answer it. GATHER: use the source-triage skill to gather sources widely
|
|
408
|
+
via web search, classify each by type and credibility, and create a
|
|
383
409
|
`source` entity for every one — including the ones you rejected, marked
|
|
384
|
-
rejected with a reason (never silently drop a source).
|
|
385
|
-
candidate answers as `hypothesis` entities
|
|
386
|
-
|
|
387
|
-
`contradicts
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
conclusion and confidence, and set the question's status to answered. Every
|
|
396
|
-
write is proposal-gated and reviewable.
|
|
410
|
+
rejected with a reason (never silently drop a source). WEIGH: form the
|
|
411
|
+
candidate answers as `hypothesis` entities that answer the question; link
|
|
412
|
+
every source that bears on a hypothesis with `supports` or
|
|
413
|
+
`contradicts`; use the evidence-synthesis skill to emit `finding`
|
|
414
|
+
entities with honestly calibrated confidence, each `supported_by` its
|
|
415
|
+
sources and `derived_from` this research; use the concept-cartography
|
|
416
|
+
skill to place the findings onto the knowledge map. ANSWER: create the
|
|
417
|
+
`research` entity that `investigates` the question and `explored` the
|
|
418
|
+
sources, write its conclusion and confidence, and set the question to
|
|
419
|
+
answered. Abandoning a question is a person's call. Every write is
|
|
420
|
+
proposal-gated and reviewable.
|
|
397
421
|
subjectProfile:
|
|
398
422
|
profileSlug: question
|
|
423
|
+
statusProperty: questionStatus
|
|
424
|
+
humanOnlyStatuses: [abandoned]
|
|
425
|
+
activators:
|
|
426
|
+
- { on: created, mode: propose }
|
|
399
427
|
params:
|
|
400
428
|
- { name: question, type: entity, label: Question, required: true }
|
|
401
429
|
executor: is-agent
|
|
@@ -406,6 +434,68 @@ playbooks:
|
|
|
406
434
|
- exa_search
|
|
407
435
|
- entity.create
|
|
408
436
|
status: active
|
|
437
|
+
requiredIntents: [search_external]
|
|
438
|
+
expectedOutputs:
|
|
439
|
+
- { kind: research, label: Research record, relationToSubject: investigates }
|
|
440
|
+
- { kind: hypothesis, label: Candidate answers, relationToSubject: answers }
|
|
441
|
+
stages:
|
|
442
|
+
- key: frame
|
|
443
|
+
name: Frame
|
|
444
|
+
category: planned
|
|
445
|
+
subjectStatus: open
|
|
446
|
+
goal: Restate the question so it can be answered and say what evidence would answer it.
|
|
447
|
+
criteria:
|
|
448
|
+
- key: question-answerable
|
|
449
|
+
statement: The question says what evidence would answer it.
|
|
450
|
+
check: { kind: judge }
|
|
451
|
+
- key: gather
|
|
452
|
+
name: Gather
|
|
453
|
+
category: started
|
|
454
|
+
subjectStatus: exploring
|
|
455
|
+
goal: Gather and classify sources, keeping every rejected source with its reason.
|
|
456
|
+
criteria:
|
|
457
|
+
- key: sources-kept
|
|
458
|
+
statement: Every source considered exists as a source entity, rejected ones with a reason.
|
|
459
|
+
check: { kind: judge }
|
|
460
|
+
- key: weigh
|
|
461
|
+
name: Weigh
|
|
462
|
+
category: started
|
|
463
|
+
goal: Form hypotheses, link evidence for and against, and emit findings with calibrated confidence.
|
|
464
|
+
criteria:
|
|
465
|
+
- key: findings-cited
|
|
466
|
+
statement: Every finding is supported by at least one source.
|
|
467
|
+
check: { kind: judge }
|
|
468
|
+
- key: counter-evidence
|
|
469
|
+
statement: Every hypothesis lists the sources that contradict it, or says none were found.
|
|
470
|
+
check: { kind: judge }
|
|
471
|
+
- key: answer
|
|
472
|
+
name: Answer
|
|
473
|
+
category: completed
|
|
474
|
+
subjectStatus: answered
|
|
475
|
+
goal: Record the research, its conclusion and confidence, and mark the question answered.
|
|
476
|
+
gate: { kind: check }
|
|
477
|
+
onFail: { toStage: weigh }
|
|
478
|
+
|
|
479
|
+
- name: Turn a finding into content
|
|
480
|
+
description: When a finding is validated, propose the posts that would share it, each linked to the finding.
|
|
481
|
+
goalTemplate: >-
|
|
482
|
+
Turn this validated finding ({finding}) into content. Read the finding,
|
|
483
|
+
its sources and the research it came from. Propose one to three posts
|
|
484
|
+
that share it with the audience — each created at the Idea status, on
|
|
485
|
+
the pillar it fits, with its hook and the reason it is worth making —
|
|
486
|
+
and link each one derived_from the finding. Quote only what the sources
|
|
487
|
+
support.
|
|
488
|
+
subjectProfile:
|
|
489
|
+
profileSlug: finding
|
|
490
|
+
statusProperty: status
|
|
491
|
+
activators:
|
|
492
|
+
- { on: enters_status, status: validated, mode: propose }
|
|
493
|
+
params:
|
|
494
|
+
- { name: finding, type: entity, label: Finding, required: true }
|
|
495
|
+
executor: is-agent
|
|
496
|
+
status: active
|
|
497
|
+
expectedOutputs:
|
|
498
|
+
- { kind: post, label: Post idea, relationToSubject: derived_from }
|
|
409
499
|
|
|
410
500
|
# A "Research this" action on every question's detail page — the entity is the
|
|
411
501
|
# starter of the flow, exactly as designed.
|
|
@@ -423,6 +513,15 @@ integrations:
|
|
|
423
513
|
- templateKey: research-methods
|
|
424
514
|
- templateKey: exa
|
|
425
515
|
|
|
516
|
+
# ── taskIntents — WHAT THIS WORKSPACE NEEDS TO FUNCTION ───────────────────
|
|
517
|
+
# Closed vocabulary: `@synap-core/types/capability-intents` (pod migrations
|
|
518
|
+
# 0283/0284). DERIVED from the declared intents of this template's own
|
|
519
|
+
# `integrations` capability packs — not hand-guessed. Checked at authoring
|
|
520
|
+
# time by `validateTemplate` (`task-intent-unknown`) and by
|
|
521
|
+
# `src/task-intents.test.ts`.
|
|
522
|
+
taskIntents:
|
|
523
|
+
- search_external
|
|
524
|
+
|
|
426
525
|
onboarding:
|
|
427
526
|
goal: >
|
|
428
527
|
Seed the workspace with the first real questions worth researching and the
|